TIOJ 1842:[IOI 2013] 王八 Wombats
TIOJ 1842:[IOI 2013] 王八 Wombats
題目大意:有一個 $n$ 行 $m$ 列的網格圖,在 $(i,j)$ 右邊有 $a_{i,j}$ 個袋熊,在 $(i,j)$ 下面有 $b_{i,j}$ 個袋熊,每次有三種操作,第一種是修改 $a_{i,j}$,第二種是修改 $b_{i,j}$,第三種是問從 $(0,x)$ 走到 $(n-1,y)$ 最少會經過幾個袋熊。$n\leq 5000,m\leq 200$,第一、二種操作最多 $500$ 次,第三種操作最多 $2\times 10^5$ 次。
解法:可以用線段樹維護從此區間前面的 $i$ 走到此區間後面的 $j$ 的距離,利用分治優化可以使兩個區間合併 $O(m^2\times\log(m))$ (其實可以 $O(m^2)$),只是如果直接開滿空間複雜度會是 $O(n\times m^2)$,所以要每 $K$ 個分一塊,當遞迴到一塊時暴力跑。
$\text{Code:}$
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx,popcnt,sse4,abm")
#include <bits/stdc++.h>
using namespace std;
#ifdef WAIMAI
#define debug(HEHE...) cout << "[" << #HEHE << "] : ", dout(HEHE)
void dout() {cout << '\n';}
template<typename T, typename...U>
void dout(T t, U...u) {cout << t << (sizeof...(u) ? ", " : ""), dout(u...);}
#else
#define debug(...) 7122
#endif
#define ll long long
#define Waimai ios::sync_with_stdio(false), cin.tie(0)
#define FOR(x,a,b) for (int x = a, I = b; x <= I; x++)
#define pb emplace_back
#define F first
#define S second
const int SIZE = 5005;
const int MSIZ = 205;
const int mxK = 8;
const int TSIZ = SIZE / mxK + 1;
int n, m, q, K, sz;
int a[SIZE][MSIZ], b[SIZE][MSIZ];
using DP = array<array<int, MSIZ>, MSIZ>;
struct Node {
DP dp;
int ls, rs;
} node[2 * TSIZ];
DP add(DP ldp, DP rdp) {
DP re;
auto divide = [&](auto divide, int p, int l, int r, int ql, int qr)->void {
if (l > r || ql == qr) {
FOR (i, l, r) re[p][i] = ldp[p][ql] + rdp[ql][i];
return;
}
int mid = (l + r) / 2, mn = INT_MAX, best;
FOR (i, ql, qr) {
int val = ldp[p][i] + rdp[i][mid];
if (val < mn) {
mn = val;
best = i;
}
}
re[p][mid] = mn;
divide(divide, p, l, mid - 1, ql, best);
divide(divide, p, mid + 1, r, best, qr);
};
FOR (i, 0, m - 1) divide(divide, i, 0, m - 1, 0, m - 1);
return re;
}
int root, cnt;
void build(int pos, int p) {
DP &dp = node[pos].dp;
int l = p * K, r = min(n - 1, (p + 1) * K);
FOR (i, 0, m - 1) {
vector<int> cur(m);
FOR (j, i + 1, m - 1) cur[j] = cur[j - 1] + a[l][j - 1];
for (int j = i - 1; j >= 0; j--) cur[j] = cur[j + 1] + a[l][j];
FOR (j, l + 1, r) {
vector<int> tmp = cur;
FOR (k, 0, m - 1) tmp[k] += b[j - 1][k];
FOR (k, 1, m - 1) tmp[k] = min(tmp[k], tmp[k - 1] + a[j][k - 1]);
for (int k = m - 2; k >= 0; k--) tmp[k] = min(tmp[k], tmp[k + 1] + a[j][k]);
cur = tmp;
}
FOR (j, 0, m - 1) dp[i][j] = cur[j];
}
}
void build(int &pos, int l, int r) {
pos = cnt++;
if (l == r) {
build(pos, l);
return;
}
int mid = (l + r) / 2;
build(node[pos].ls, l, mid);
build(node[pos].rs, mid + 1, r);
node[pos].dp = add(node[node[pos].ls].dp, node[node[pos].rs].dp);
}
void upd(int pos, int l, int r, int p) {
if (l == r) {
build(pos, l);
return;
}
int mid = (l + r) / 2;
if (p <= mid) upd(node[pos].ls, l, mid, p);
else upd(node[pos].rs, mid + 1, r, p);
node[pos].dp = add(node[node[pos].ls].dp, node[node[pos].rs].dp);
}
void solve() {
cin >> n >> m;
if (m <= 100) K = 32;
else K = 8;
FOR (i, 0, n - 1) FOR (j, 0, m - 2) cin >> a[i][j];
FOR (i, 0, n - 2) FOR (j, 0, m - 1) cin >> b[i][j];
sz = (n + K - 1) / K;
build(root, 0, sz - 1);
cin >> q;
while (q--) {
int t, x, y, w;
cin >> t >> x >> y;
if (t <= 2) {
cin >> w;
(t == 1 ? a[x][y] : b[x][y]) = w;
upd(root, 0, sz - 1, x / K);
if (x > 0 && x % K == 0) upd(root, 0, sz - 1, x / K - 1);
} else {
cout << node[root].dp[x][y] << '\n';
}
}
}
int main() {
Waimai;
solve();
}
我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!
留言
張貼留言