题解:P6794 [SNOI2020] 水池

· · 题解

提供一种分块做法。

注意到 0 操作就是给从 x 左边第一个高度大于 h 挡板到 x 右边第一个高度小于 h 的挡板中间的水池高度赋值为 h。区间赋值,分块可以直接维护。

1 操作就是把每一个水池的高度和它到 x 水池中间最高挡板高度 chkmin。

注意到 1 操作是一个类似单调栈的结构,考虑分块去维护。

给每一个块打一个 lchk,rchk 标记,表示这个块的左侧/右侧有排水操作的影响需要更新,记录这些排水操作的排水点到当前块的边界最高挡板的高度最小值。

那么 1 操作的时候只需要把排水点所在块暴力修改,其他块直接打标记。

修改挡板高度时直接把对应块重构。查询一个点水深的时候直接把它所在的块标记下放,就可以查到正确值。

至于可持久化,由于本题可以离线,使用操作树+回滚可以维护,注意尽量节约使用空间,没必要放入回滚栈中的元素就不要放。

## code 为了图方便,实现的比较丑陋(比如找赋值区间使用线段树外二分)。 ```cpp #include<bits/stdc++.h> #include<bits/extc++.h> using namespace std; using ll = long long; using ull = unsigned long long; #define ld cin #define jyt cout #define REP(i, l, r) for (int i = l; i <= r; ++i) #define PER(i, l, r) for (int i = l; i >= r; --i) #define clr(name, n) memset(name, 0, ((n) + 1) * sizeof(name[0])) #define mem(name, v, n) memset(name, v, ((n) + 1) * sizeof(name[0])) const int N = 2e5 + 7, B = 450; const int inf = 1e9 + 7; const int P = 998244353; namespace JoKing { // JoKing 10:56 60~70pts int n, W, q, a[N], h[N], lmx[N], rmx[N], bel[N], L[N], R[N], tage[N], lchk[N], rchk[N]; vector<int> G[N]; pair<int*, int> stc[50000000]; int Ans[N], top = 0; struct Opera {int op, id, x, h;} c[N]; struct Node { int lc, rc, mx; #define ls(x) tr[x].lc #define rs(x) tr[x].rc } tr[N * 30]; int Rt[N], Trc = 0; inline void blt(int &x, int l, int r) { if (x = ++Trc, l == r) return void(tr[x].mx = h[l]); int mid = (l + r) >> 1; blt(ls(x), l, mid), blt(rs(x), mid + 1, r), tr[x].mx = max(tr[ls(x)].mx, tr[rs(x)].mx); } inline void upd(int &x, int l, int r, int now, int c) { if (tr[++Trc] = tr[x], x = Trc, l == r) return void(tr[x].mx = c); int mid = (l + r) >> 1; (now <= mid ? upd(ls(x), l, mid, now, c) : upd(rs(x), mid + 1, r, now, c)), tr[x].mx = max(tr[ls(x)].mx, tr[rs(x)].mx); } inline int ask(int x, int l, int r, int L, int R) { if (L <= l && r <= R) return tr[x].mx; int mid = (l + r) >> 1, c = -1; if (L <= mid) c = max(c, ask(ls(x), l, mid, L, R)); if (mid < R) c = max(c, ask(rs(x), mid + 1, r, L, R)); return c; } inline void pushtag(int Biden) { if (tage[Biden] != -1 || lchk[Biden] != -1 || rchk[Biden] != -1) REP(i, L[Biden], R[Biden]) stc[++top] = {&a[i], a[i]}; if (tage[Biden] != -1) {REP(i, L[Biden], R[Biden]) a[i] = tage[Biden]; stc[++top] = {&tage[Biden], tage[Biden]}; tage[Biden] = -1;} if (lchk[Biden] != -1) {REP(i, L[Biden], R[Biden]) a[i] = min(a[i], max(lchk[Biden], lmx[i])); stc[++top] = {&lchk[Biden], lchk[Biden]}; lchk[Biden] = -1;} if (rchk[Biden] != -1) {REP(i, L[Biden], R[Biden]) a[i] = min(a[i], max(rchk[Biden], rmx[i])); stc[++top] = {&rchk[Biden], rchk[Biden]}; rchk[Biden] = -1;} } inline void rebuild(int Biden) { if (lmx[L[Biden]] != 0) stc[++top] = {&lmx[L[Biden]], lmx[L[Biden]]}; lmx[L[Biden]] = 0; REP(i, L[Biden] + 1, R[Biden]) { if (lmx[i] != max(lmx[i - 1], h[i - 1])) stc[++top] = {&lmx[i], lmx[i]}; lmx[i] = max(lmx[i - 1], h[i - 1]); } if (rmx[R[Biden]] != h[R[Biden]]) stc[++top] = {&rmx[R[Biden]], rmx[R[Biden]]}; rmx[R[Biden]] = h[R[Biden]]; PER(i, R[Biden] - 1, L[Biden]) { if (rmx[i] != max(rmx[i + 1], h[i])) stc[++top] = {&rmx[i], rmx[i]}; rmx[i] = max(rmx[i + 1], h[i]); } } inline int findl(int Rt, int now, int c) { int L = 1, R = now, Res = 0; while (L <= R) { int mid = (L + R) >> 1, w = ask(Rt, 1, n, mid, now); if (w >= c) L = mid + 1, Res = mid; else R = mid - 1; } return Res; } inline int findr(int Rt, int now, int c) { int L = now, R = n, Res = n; while (L <= R) { int mid = (L + R) >> 1, w = ask(Rt, 1, n, now, mid); if (w >= c) R = mid - 1, Res = mid; else L = mid + 1; } return Res; } inline void TAGE(int l, int r, int c) { int S = bel[l], T = bel[r]; if (S == T) {pushtag(S); REP(i, l, r) (a[i] != c && (stc[++top] = {&a[i], a[i]}, 1)), a[i] = c; return;} pushtag(S), pushtag(T); REP(i, l, R[S]) (a[i] != c && (stc[++top] = {&a[i], a[i]}, 1)), a[i] = c; REP(i, S + 1, T - 1) { (lchk[i] != -1 && (stc[++top] = {&lchk[i], lchk[i]}, 1)); (rchk[i] != -1 && (stc[++top] = {&rchk[i], rchk[i]}, 1)); (tage[i] != c && (stc[++top] = {&tage[i], tage[i]}, 1)); lchk[i] = rchk[i] = -1, tage[i] = c; } REP(i, L[T], r) (a[i] != c && (stc[++top] = {&a[i], a[i]}, 1)), a[i] = c; } inline void LCHK(int now) { int Trump = bel[now], mx = h[now]; stc[++top] = {&a[now], a[now]}, a[now] = 0; REP(i, now + 1, R[Trump]) (a[i] != min(a[i], mx) && (stc[++top] = {&a[i], a[i]}, 1)), a[i] = min(a[i], mx), mx = max(mx, h[i]); REP(i, Trump + 1, W) { if (lchk[i] != -1 && lchk[i] <= mx) break; (lchk[i] != mx && (stc[++top] = {&lchk[i], lchk[i]}, 1)); lchk[i] = mx, mx = max(mx, rmx[L[i]]); } } inline void RCHK(int now) { int Trump = bel[now], mx = 0; stc[++top] = {&a[now], a[now]}, a[now] = 0; PER(i, now - 1, L[Trump]) mx = max(mx, h[i]), (a[i] != min(a[i], mx) && (stc[++top] = {&a[i], a[i]}, 1)), a[i] = min(a[i], mx); PER(i, Trump - 1, 1) { if (rchk[i] != -1 && rchk[i] <= mx) break; (rchk[i] != mx && (stc[++top] = {&rchk[i], rchk[i]}, 1)); rchk[i] = mx, mx = max(mx, rmx[L[i]]); } } inline void dfs(int x, bool need = false) { need |= (G[x].size() > 1); for (int i : G[x]) { int his = top; if (c[i].op == 0) { if (pushtag(bel[c[i].x]), c[i].h > a[c[i].x]) TAGE(findl(Rt[i], c[i].x - 1, c[i].h) + 1, findr(Rt[i], c[i].x, c[i].h), c[i].h); } else if (c[i].op == 1) { pushtag(bel[c[i].x]), LCHK(c[i].x), RCHK(c[i].x); } else if (c[i].op == 2) { stc[++top] = {&h[c[i].x], h[c[i].x]}; h[c[i].x] = c[i].h, pushtag(bel[c[i].x]), rebuild(bel[c[i].x]); } else { pushtag(bel[c[i].x]), Ans[i] = a[c[i].x]; } if (!need) top = 0; dfs(i, need); while (top != his) *stc[top].first = stc[top].second, --top; } } signed main() { bool flag = true, CZXD = true; ld >> n >> q, h[n] = inf; REP(i, 1, n - 1) ld >> h[i]; REP(i, 1, q) ld >> c[i].op >> c[i].id >> c[i].x, ((c[i].op % 2 == 0) && (ld >> c[i].h, 1)), flag &= (c[i].id == i - 1), CZXD &= (c[i].op != 1); blt(Rt[0], 1, n); REP(i, 1, q) G[c[i].id].emplace_back(i), Rt[i] = Rt[c[i].id], (c[i].op == 2 && (upd(Rt[i], 1, n, c[i].x, c[i].h), 1)); for (int l = 1, r = min(n, B); l <= n; l += B, r = min(n, r + B)) fill(bel + l, bel + r + 1, ++W), L[W] = l, R[W] = r, tage[W] = lchk[W] = rchk[W] = -1; REP(i, 1, W) rebuild(i); top = 0, dfs(0); REP(i, 1, q) if (c[i].op == 3) jyt << Ans[i] << '\n'; return 0; } } signed main() { // 400mb 0.9s #ifdef WYY freopen("water3.in", "r", stdin); freopen("water.out", "w", stdout); #else // freopen("water3.in", "r", stdin); // freopen("water.out", "w", stdout); #endif JoKing::main(); #ifdef WYY // while(true); #endif return 0; } ```