Template - 「整體二分」
阿新 • • 發佈:2022-02-17
寫的簡單。主要是留給自己做複習資料。
「BZOJ1901」Dynamic Rankings.
給定一個含有 \(n\) 個數的序列 \(a_1,a_2 \dots a_n\),需要支援兩種操作:
Q l r k
表示查詢下標在區間 \([l,r]\) 中的第 \(k\) 小的數。C x y
表示將 \(a_x\) 改為 \(y\)。
引入整體二分。
其實就是我們對於二分到的一個值域 mid,去離線針對所有的詢問進行 check。
具體是利用一些資料結構。
然後根據 check 和 mid 的大小將詢問分為兩撥,再分治下去解決問題。
參考於 OI-wiki 的板子很精簡且思路便於理解。
#include <cstdio> int Abs (int x) { return x < 0 ? -x : x; } int Max (int x, int y) { return x > y ? x : y; } int Min (int x, int y) { return x < y ? x : y; } int Read () { int x = 0, k = 1; char s = getchar(); while (s < '0' || s > '9') { if(s == '-') k = -1; s = getchar (); } while ('0' <= s && s <= '9') x = (x << 3) + (x << 1) + (s ^ 48), s = getchar (); return x * k; } void Write (int x) { if(x < 0) x = -x, putchar ('-'); if(x > 9) Write (x / 10); putchar (x % 10 + '0'); } void Print (int x, char s) { Write (x), putchar (s); } const int MAXN = 2e5 + 5; char Opt[3]; int Ans[MAXN], BIT[MAXN], a[MAXN], n, m; int Low_Bit (int x) { return x & -x; } void Update (int k, int x) { for (int i = k; i <= n; i += Low_Bit (i)) BIT[i] += x; } int Query (int k) { int Res = 0; for (int i = k; i >= 1; i -= Low_Bit (i)) Res += BIT[i]; return Res; } struct Node { bool Type; int Id, x, y, k; Node () {} Node (bool T, int I, int X, int Y, int K) { Type = T, Id = I, x = X, y = Y, k = K; } } q[MAXN], Tmp[2][MAXN]; void Solve (int l, int r, int L, int R) { if (l > r || L > R) return ; if (L == R) { for (int i = l; i <= r; i++) if (q[i].Type) Ans[q[i].Id] = L; return ; } int Mid = (L + R) >> 1, Cnt0 = 0, Cnt1 = 0; for (int i = l; i <= r; i++) if (q[i].Type) { int Check = Query (q[i].y) - Query (q[i].x - 1); if (q[i].k <= Check) Tmp[0][++Cnt0] = q[i]; else q[i].k -= Check, Tmp[1][++Cnt1] = q[i]; } else { if (q[i].y <= Mid) Update (q[i].x, q[i].k), Tmp[0][++Cnt0] = q[i]; else Tmp[1][++Cnt1] = q[i]; } for (int i = 1; i <= Cnt0; i++) if (!Tmp[0][i].Type) Update (Tmp[0][i].x, -Tmp[0][i].k); for (int i = 1; i <= Cnt0; i++) q[l + i - 1] = Tmp[0][i]; for (int i = 1; i <= Cnt1; i++) q[l + Cnt0 + i - 1] = Tmp[1][i]; Solve (l, l + Cnt0 - 1, L, Mid), Solve (l + Cnt0, r, Mid + 1, R); } int main () { n = Read (), m = Read (); int p = 0, Tot = 0; for (int i = 1, x; i <= n; i++) x = Read (), q[++p] = Node (0, 0, i, x, 1), a[i] = x; for (int i = 1, x, y; i <= m; i++) { scanf ("%s", Opt + 1), x = Read (), y = Read (); if (Opt[1] == 'Q') { int k = Read (); q[++p] = Node (1, ++Tot, x, y, k); } else q[++p] = Node (0, 0, x, a[x], -1), q[++p] = Node (0, 0, x, y, 1), a[x] = y; } Solve (1, p, 0, 1e9); for (int i = 1; i <= Tot; i++) Print (Ans[i], '\n'); return 0; }
「ZJOI2013」K 大數查詢
有 \(n\) 個位置,\(m\) 個操作。操作有兩種:
1 a b c
表示在第 \(a\) 個位置到第 \(b\) 個位置,每個位置加入一個數 \(c\)2 a b c
表示詢問從第 \(a\) 個位置到第 \(b\) 個位置,問第 \(c\) 大的數是多少。
放在這裡是因為發現它比上道題的唯一差別在於區間修改。
也就是說整體二分的資料結構理論上可以百搭。比如線段樹。
#include <cstdio> typedef long long LL; int Abs (int x) { return x < 0 ? -x : x; } int Max (int x, int y) { return x > y ? x : y; } int Min (int x, int y) { return x < y ? x : y; } int Read () { int x = 0, k = 1; char s = getchar(); while (s < '0' || s > '9') { if(s == '-') k = -1; s = getchar (); } while ('0' <= s && s <= '9') x = (x << 3) + (x << 1) + (s ^ 48), s = getchar (); return x * k; } LL Read_LL () { LL x = 0, k = 1; char s = getchar(); while (s < '0' || s > '9') { if(s == '-') k = -1; s = getchar (); } while ('0' <= s && s <= '9') x = (x << 3) + (x << 1) + (s ^ 48), s = getchar (); return x * k; } void Write (int x) { if(x < 0) x = -x, putchar ('-'); if(x > 9) Write (x / 10); putchar (x % 10 + '0'); } void Print (int x, char s) { Write (x), putchar (s); } const int MAXN = 5e4 + 5; int Ans[MAXN], n, m; struct Segment_Tree { #define Lson p << 1 #define Rson p << 1 | 1 struct Segment_Node { int l, r; bool Clear; LL Sum, Lazy; Segment_Node () {} Segment_Node (int L, int R, LL S, LL La, bool C) { l = L, r = R, Sum = S, Lazy = La, Clear = C; } } Tr[MAXN * 4]; void Push (int p) { if (Tr[p].Clear) { Tr[Lson].Sum = Tr[Rson].Sum = 0; Tr[Lson].Lazy = Tr[Rson].Lazy = 0; Tr[Lson].Clear = Tr[Rson].Clear = true; Tr[p].Clear = false; } if (Tr[p].Lazy) { Tr[Lson].Sum += Tr[p].Lazy * (Tr[Lson].r - Tr[Lson].l + 1); Tr[Rson].Sum += Tr[p].Lazy * (Tr[Rson].r - Tr[Rson].l + 1); Tr[Lson].Lazy += Tr[p].Lazy, Tr[Rson].Lazy += Tr[p].Lazy; Tr[p].Lazy = 0; } } void Pull (int p) { Tr[p].Sum = Tr[Lson].Sum + Tr[Rson].Sum; } void Make_Tree (int p, int l, int r) { Tr[p].l = l, Tr[p].r = r; if (Tr[p].l == Tr[p].r) return ; int Mid = (Tr[p].l + Tr[p].r) >> 1; Make_Tree (Lson, l, Mid); Make_Tree (Rson, Mid + 1, r); } void Update (int p, int l, int r, LL x) { if (l <= Tr[p].l && Tr[p].r <= r) { Tr[p].Sum += x * (Tr[p].r - Tr[p].l + 1), Tr[p].Lazy += x; return ; } Push (p); int Mid = (Tr[p].l + Tr[p].r) >> 1; if (l <= Mid) Update (Lson, l, r, x); if (r > Mid) Update (Rson, l, r, x); Pull (p); } LL Query (int p, int l, int r) { if (l <= Tr[p].l && Tr[p].r <= r) return Tr[p].Sum; Push (p); int Mid = (Tr[p].l + Tr[p].r) >> 1; LL Res = 0; if (l <= Mid) Res += Query (Lson, l, r); if (r > Mid) Res += Query (Rson, l, r); Pull (p); return Res; } #undef Lson #undef Rson } Seg; struct Node { LL k; bool Type; int Id, x, y; Node () {} Node (bool T, int I, int X, int Y, LL K) { Type = T, Id = I, x = X, y = Y, k = K; } } q[MAXN], Tmp[2][MAXN]; void Solve (int l, int r, int L, int R) { if (l > r || L > R) return ; if (L == R) { for (int i = l; i <= r; i++) if (q[i].Type) Ans[q[i].Id] = L; return ; } int Mid = (L + R) >> 1, Cnt0 = 0, Cnt1 = 0; Seg.Tr[1].Clear = true, Seg.Tr[1].Sum = Seg.Tr[1].Lazy = 0; for (int i = l; i <= r; i++) if (q[i].Type) { LL Check = Seg.Query(1, q[i].x, q[i].y); if (q[i].k <= Check) Tmp[1][++Cnt1] = q[i]; else q[i].k -= Check, Tmp[0][++Cnt0] = q[i]; } else { if (q[i].k > Mid) Seg.Update (1, q[i].x, q[i].y, 1ll), Tmp[1][++Cnt1] = q[i]; else Tmp[0][++Cnt0] = q[i]; } for (int i = 1; i <= Cnt0; i++) q[l + i - 1] = Tmp[0][i]; for (int i = 1; i <= Cnt1; i++) q[l + Cnt0 + i - 1] = Tmp[1][i]; Solve (l, l + Cnt0 - 1, L, Mid), Solve (l + Cnt0, r, Mid + 1, R); } int main () { LL k; n = Read (), m = Read (); int p = 0, Tot = 0; Seg.Make_Tree (1, 1, n); for (int i = 1, Opt, x, y; i <= m; i++) { Opt = Read (), x = Read (), y = Read (), k = Read_LL (); if (Opt == 1) q[++p] = Node (0, i, x, y, k); else q[++p] = Node (1, ++Tot, x, y, k); } Solve (1, p, -n, n); for (int i = 1; i <= Tot; i++) Print (Ans[i], '\n'); return 0; }