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}$,歡迎追蹤!

留言

熱門文章