C++ 指標和引用
阿新 • • 發佈:2021-01-18
樹狀陣列套平衡樹
可以解決區間的可減問題, 比如區間有多少個大於等於/小於等於 k 的數。
對於 [這道題], 其實也可以用樹狀陣列套平衡樹, 不過要外層二分多 log, 平衡樹比較難多樹二分。
具體地, 對於修改, 拆成插入刪除;對於排名直接做;對於前驅後繼 kth, 都可以用外層套二分做。
整體的複雜度, 空間是 O(n log n), 時間是 O(q log3 n) 的。
具體說說前驅後繼二分的細節, 對於一個數 x 的前驅, 是 [0,x-1] 中最大的和不同的 x “大於等於其的有多少” 的數, 後繼也差不多, 比較糊地說一下就是 x 往後找後第一個和 x 的 “小於等於其的有多少” 的數值不同的數。
#include<bits/stdc++.h> #define INF 1e9 using namespace std; const int N = 5e4 + 3, SZ = (5e4+23) * 2 * 16 + 2333; int cnt; #define newnode(s, v, a, b) (&(*pool[cnt++] = node(s, v, a, b))) #define merge(a, b) newnode(a->siz+b->siz, b->val, a, b) #define upd(me) if(me->ls->siz) me->siz=me->ls->siz+me->rs->siz, me->val=me->rs->val struct node{ int siz, val; node *ls, *rs; node(int s, int v, node *a, node *b) : siz(s), val(v), ls(a), rs(b) { } node() { } } *root[N], *pool[SZ], t[SZ], *emp; inline void maintain(node *me) { if(me->ls->siz > me->rs->siz * 4) me->rs = merge(me->ls->rs, me->rs), pool[--cnt] = me->ls, me->ls = me->ls->ls; if(me->rs->siz > me->ls->siz * 4) me->ls = merge(me->ls, me->rs->ls), pool[--cnt] = me->rs, me->rs = me->rs->rs; } void ins(int x, node *me) { if(me->siz == 1) me->ls = newnode(1, min(x, me->val), emp, emp), me->rs = newnode(1, max(x, me->val), emp, emp); else ins(x, x>me->ls->val ? me->rs : me->ls), maintain(me); upd(me); } void del(int x, node *me) { if(me->ls->siz == 1 && me->ls->val == x) pool[--cnt] = me->ls, pool[--cnt] = me->rs, *me = *me->rs; else if(me->rs->siz == 1 && me->rs->val == x) pool[--cnt] = me->ls, pool[--cnt] = me->rs, *me = *me->ls; else del(x, x>me->ls->val ? me->rs : me->ls), maintain(me); upd(me); } int low(int x, node *me) { if(me->siz == 1) return (me->val <= x); else return x>=me->ls->val ? me->ls->siz + low(x, me->rs) : low(x, me->ls); } int upp(int x, node *me) { return me->siz - low(x-1, me); } int n, m, a[N]; void ins(int x, int v) { for(; x<=n; x += (x&(-x))) ins(v, root[x]); } void del(int x, int v) { for(; x<=n; x += (x&(-x))) del(v, root[x]); } int low(int x, int v) { int res = 0; for(; x; x-=(x&(-x))) res += low(v, root[x]); return res; } int low(int l, int r, int v) { return low(r, v) - low(l-1, v); } int upp(int x, int v) { int res = 0; for(; x; x-=(x&(-x))) res += upp(v, root[x]); return res; } int upp(int l, int r, int v) { return upp(r, v) - upp(l-1, v); } int main() { emp = new node(0, 0, NULL, NULL); for(int i=0; i<SZ; ++i) pool[i] = &t[i]; scanf("%d%d", &n, &m); for(int i=1; i<=n; ++i) root[i] = newnode(1, INF, emp, emp); for(int i=1; i<=n; ++i) scanf("%d", &a[i]), ins(i, a[i]); while(m--) { int opt; scanf("%d", &opt); if(opt == 1) { int l, r, k; scanf("%d%d%d", &l, &r, &k); cout << low(l, r, k-1) + 1 << '\n'; } if(opt == 2) { int l, r, k; scanf("%d%d%d", &l, &r, &k); int L=0, R=100000000; while(L != R) { int mid = (L+R+1) >> 1; if(low(l, r, mid-1) + 1 <= k) L = mid; else R = mid-1; } cout << L << '\n'; } if(opt == 3) { int pos, k; scanf("%d%d", &pos, &k); del(pos, a[pos]); ins(pos, (a[pos] = k)); } if(opt == 4) { int l, r, k; scanf("%d%d%d", &l, &r, &k); if(k == 0) { puts("-2147483647"); continue; } int L=-1, R=k-1, tmp = upp(l, r, k); while(L!=R) { int mid = (L+R+1) >> 1; if(upp(l, r, mid) != tmp) L = mid; else R = mid-1; } if(L == -1) puts("-2147483647"); else cout << L << '\n'; } if(opt == 5) { int l, r, k; scanf("%d%d%d", &l, &r, &k); if(k == 100000000) { puts("2147483647"); continue; } int L=k+1, R=100000000+1, tmp = low(l, r, k); while(L!=R) { int mid = (L+R) >> 1; if(low(l, r, mid) != tmp) R = mid; else L = mid+1; } if(L == 100000001) puts("2147483647"); else cout << L << '\n'; } } return 0; }