题意:写一个数据结构支持如下操作:
(1)区间第k大
(2)区间内求某个数的排名
(3)修改某个位置的数
(4)区间内求某个数的前趋、后继。
Sol:以下提供两种做法。
Sol1:线段树套平衡树。非常裸的做法,除询问区间第k大复杂度为O(log^3n),其余操作时间复杂度为O(log^2n).
Code1:
#include<cstdio> #include<cstring> #include<cstdlib> #define INF (100000000) #define l(x) S[x].l #define r(x) S[x].r #define v(x) S[x].v #define cnt(x) S[x].cnt #define p(x) S[x].p #define s(x) S[x].s #define lson(x) Q[x].lson #define rson(x) Q[x].rson #define dl(x) Q[x].dl #define dr(x) Q[x].dr #define root(x) Q[x].root const int N = 50001; inline int _min(int a , int b) { return a < b ? a : b; } inline int _max(int a , int b) { return a > b ? a : b; } struct Node { int l , r , v , cnt , p , s; }S[2000050]; int Node_Ind; int newnode(int x) { int q = ++Node_Ind; v(q) = x; cnt(q) = 1; p(q) = rand(); s(q) = 1; return q; } void maintain(int &q) { s(q) = s(l(q)) + cnt(q) + s(r(q)); } void lr(int &q) { int tmp = r(q); r(q) = l(tmp); l(tmp) = q; maintain(q); maintain(tmp); q = tmp; } void rr(int &q) { int tmp = l(q); l(q) = r(tmp); r(tmp) = q; maintain(q); maintain(tmp); q = tmp; } void insert(int x , int &q) { if (!q) q = newnode(x); else { if (x == v(q)) ++cnt(q); else if (x < v(q)) { insert(x , l(q)); if (p(q) < p(l(q))) rr(q); } else { insert(x , r(q)); if (p(q) < p(r(q))) lr(q); } } maintain(q); } void remove(int x , int &q) { if (!q) return; if (x == v(q)) { if (cnt(q) > 1) --cnt(q); else if (!l(q) || !r(q)) q = (!l(q)) ? r(q) : l(q); else if (p(l(q)) < p(r(q))) { lr(q); remove(x , l(q)); } else { rr(q); remove(x , r(q)); } } else if (x < v(q)) remove(x , l(q)); else remove(x , r(q)); if (q) maintain(q); } int getprev(int x , int &q) { int ans = -1 << 30; int ins = q; while(ins) { if (v(ins) >= x) ins = l(ins); else { ans = v(ins); ins = r(ins); } } return ans; } int getnext(int x , int &q) { int ans = 1 << 30; int ins = q; while(ins) { if (v(ins) <= x) ins = r(ins); else { ans = v(ins); ins = l(ins); } } return ans; } int getrank(int x , int &q) { int ans = 0; int ins = q; while(ins) { if (x <= v(ins)) ins = l(ins); else { ans += s(l(ins)) + cnt(ins); ins = r(ins); } } return ans; } int num[N]; struct Segment_Node { int lson , rson , dl , dr , root; }; struct Segment_Tree { Segment_Node Q[150000]; int ind; Segment_Tree() { memset(Q , 0 , sizeof(Q)); ind = 0; } int build(int tl , int tr) { int q = ++ind; dl(q) = tl; dr(q) = tr; for(register int i = tl ; i <= tr ; ++i) insert(num[i] , root(q)); if (tl == tr) return q; int mid = (tl + tr) >> 1; lson(q) = build(tl , mid); rson(q) = build(mid + 1 , tr); return q; } int Seg_getrank(int tl , int tr , int x , int q = 1) { if (tl <= dl(q) && dr(q) <= tr) return getrank(x , root(q)); int mid = (dl(q) + dr(q)) >> 1; if (tl > mid) return Seg_getrank(tl , tr , x , rson(q)); else if (tr <= mid) return Seg_getrank(tl , tr , x , lson(q)); else return Seg_getrank(tl , mid , x , lson(q)) + Seg_getrank(mid + 1 , tr , x , rson(q)); } int Seg_getkth(int tl , int tr , int k) { int L , R , mid; L = 0 , R = INF , mid = (L + R + 1 )>> 1; while(L < R) { if (Seg_getrank(tl , tr , mid) < k) L = mid; else R = mid - 1; mid = (L + R + 1) >> 1; } return mid; } void modify(int ins , int val , int q = 1) { remove(num[ins] , root(q)); insert(val , root(q)); if (dl(q) == dr(q)) return; int mid = (dl(q) + dr(q)) >> 1; modify(ins , val , (ins <= mid) ? lson(q) : rson(q)); } int Seg_getprev(int tl , int tr , int x , int q = 1) { if (tl <= dl(q) && dr(q) <= tr) return getprev(x , root(q)); int mid = (dl(q) + dr(q)) >> 1; if (tl > mid) return Seg_getprev(tl , tr , x , rson(q)); else if (tr <= mid) return Seg_getprev(tl , tr , x , lson(q)); else return _max(Seg_getprev(tl , mid , x , lson(q)) , Seg_getprev(mid + 1 , tr ,x , rson(q))); } int Seg_getnext(int tl , int tr , int x , int q = 1) { if (tl <= dl(q) && dr(q) <= tr) return getnext(x , root(q)); int mid = (dl(q) + dr(q)) >> 1; if (tl > mid) return Seg_getnext(tl , tr , x , rson(q)); else if (tr <= mid) return Seg_getnext(tl , tr , x , lson(q)); else return _min(Seg_getnext(tl ,mid , x , lson(q)) , Seg_getnext(mid + 1 , tr , x , rson(q))); } }Ans; int main() { int n , m; scanf("%d%d" , &n , &m); register int i; for(i = 1 ; i <= n ; ++i) scanf("%d" , &num[i]); Ans.build(1 , n); int sign , a , b , x; for(i = 1 ; i <= m; ++i) { scanf("%d" , &sign); switch (sign) { case 1: { scanf("%d%d%d" , &a , &b , &x); printf("%d\n" , Ans.Seg_getrank(a , b , x) + 1); break; } case 2: { scanf("%d%d%d" , &a , &b , &x); printf("%d\n" , Ans.Seg_getkth(a , b , x)); break; } case 3: { scanf("%d%d" , &a , &x); Ans.modify(a , x); num[a] = x; break; } case 4: { scanf("%d%d%d" , &a , &b , &x); printf("%d\n" , Ans.Seg_getprev(a , b , x)); break; } case 5: { scanf("%d%d%d" , &a , &b , &x); printf("%d\n" , Ans.Seg_getnext(a , b , x)); break; } } } return 0; }
回忆树状数组:节点i存储的是以位置i为结尾,长度为lowbit(i)的连续一段的信息。树状数组依赖于信息满足区间减法。
求前缀和,只需从位置i开始依次找到上一段并求和即可。
修改某位置:令i不断加上lowbit(i)得到下一段应该被修改的是哪一段,画个图就容易理解了。
回忆主席树:在值域在很小的范围内或者允许离线的情况下,建立可持久化权值线段树。版本i可认为是前i个位置的数所构成的权值线段树。
新版本的权值线段树只需在上一个版本的基础上新建一条路径上的O(logn)个节点即可。
那么求区间第k大只需在差分后的权值线段树上根据size决定向左或向右走即可,避免了二分。
如果支持修改,那么朴素的主席树无法解决问题了。
我们重新定义:类似树状数组,第i个版本表示第i-lowbit(i)+1~i个位置的数所构成的权值线段树。
那么我们询问区间第k大的时候,以两个位置前缀和的size差值作为依据决定走向左子树或右子树。复杂度O(log^2n).
修改时,新建logn颗权值线段树的新版本,复杂度O(log^2n).
Code2:
#include <cstdio> #include <cstring> #include <cctype> #include <iostream> #include <algorithm> using namespace std; inline int getc() { static const int L = 1 << 20; static char buf[L], *S = buf, *T = buf; if (S == T) { T = (S = buf) + fread(buf, 1, L, stdin); if (S == T) return EOF; } return *S++; } inline int getint() { int c; while(!isdigit(c = getc()) && c != '-'); bool sign = c == '-'; int tmp = sign ? 0 : c - '0'; while(isdigit(c = getc())) tmp = (tmp << 1) + (tmp << 3) + c - '0'; return sign ? -tmp : tmp; } #define SORT(S, n) sort(S + 1, S + n + 1) #define N 50010 #define M 50010 int w[N], ope[M][4]; int Global_weigh[N + M], rank_weigh[N + M], top, id; int getins(int x) { int L = 1, R = id, mid; while(L < R) { mid = (L + R) >> 1; if (rank_weigh[mid] < x) L = mid + 1; else R = mid; } return L; } #define l(x) S[x].l #define r(x) S[x].r #define size(x) S[x].size struct Node { int l, r; short size; }S[8000000]; int ind; void Newadd(int Last, int &q, int dl, int dr, int ins, int add) { if (!q) q = ++ind; S[q] = S[Last]; size(q) += add; if (dl == dr) return; int mid = (dl + dr) >> 1; if (ins <= mid) Newadd(l(Last), l(q), dl, mid, ins, add); else Newadd(r(Last), r(q), mid + 1, dr, ins, add); } int build(int dl, int dr) { int q = ++ind; if (dl == dr) return q; int mid = (dl + dr) >> 1; l(q) = build(dl, mid); r(q) = build(mid + 1, dr); return q; } int root[N], bit[N]; int count(int x, bool self) { int res = 0; for(; x; x -= x & -x) res += self ? size(bit[x]) : size(l(bit[x])); return res; } inline void setself(int x) { for(; x; x -= x & -x) bit[x] = root[x]; } inline void setson(int x, bool d) { for(; x; x -= x & -x) bit[x] = d ? r(bit[x]) : l(bit[x]); } int getless(int dl, int dr, int ins, int a, int b) { if (dl == dr) return dl < ins ? count(b, 1) - count(a, 1) : 0; int mid = (dl + dr) >> 1; if (ins <= mid) { setson(a, 0), setson(b, 0); return getless(dl, mid, ins, a, b); } else { int res = count(b, 0) - count(a, 0); setson(a, 1), setson(b, 1); return res + getless(mid + 1, dr, ins, a, b); } } int getkth(int dl, int dr, int k, int a, int b) { if (dl == dr) return dl; int mid = (dl + dr) >> 1; int ls = count(b, 0) - count(a, 0); if (ls >= k) { setson(a, 0), setson(b, 0); return getkth(dl, mid, k, a, b); } else { setson(a, 1), setson(b, 1); return getkth(mid + 1, dr, k - ls, a, b); } } int getnum(int dl, int dr, int ins, int a, int b) { if (dl == dr) return count(b, 1) - count(a, 1); int mid = (dl + dr) >> 1; if (ins <= mid) { setson(a, 0), setson(b, 0); return getnum(dl, mid, ins, a, b); } else { setson(a, 1), setson(b, 1); return getnum(mid + 1, dr, ins, a, b); } } int main() { int n = getint(), m = getint(); register int i, j; for(i = 1; i <= n; ++i) Global_weigh[++top] = w[i] = getint(); for(i = 1; i <= m; ++i) { ope[i][0] = getint(); if (ope[i][0] == 3) { ope[i][1] = getint(), ope[i][3] = getint(); Global_weigh[++top] = ope[i][3]; } else { ope[i][1] = getint(), ope[i][2] = getint(), ope[i][3] = getint(); if (ope[i][0] != 2) Global_weigh[++top] = ope[i][3]; } } //Offline-Preatments SORT(Global_weigh, top); Global_weigh[0] = -1 << 30; for(i = 1; i <= top; ++i) if (Global_weigh[i] != Global_weigh[i - 1]) rank_weigh[++id] = Global_weigh[i]; for(i = 1; i <= n; ++i) w[i] = getins(w[i]); for(i = 1; i <= m; ++i) if (ope[i][0] != 2) ope[i][3] = getins(ope[i][3]); //Build the Init President Tree root[0] = build(1, id); for(i = 1; i <= n; ++i) root[i] = ++ind; for(i = 1; i <= n; ++i) for(j = i; j <= n; j += j & -j) Newadd(root[j], root[j], 1, id, w[i], 1); //Answer the Questions for(i = 1; i <= m; ++i) { if (ope[i][0] == 1) {//get-rank setself(ope[i][1] - 1); setself(ope[i][2]); printf("%d\n", getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]) + 1); } else if (ope[i][0] == 2) {//get-kth setself(ope[i][1] - 1); setself(ope[i][2]); printf("%d\n", rank_weigh[getkth(1, id, ope[i][3], ope[i][1] - 1, ope[i][2])]); } else if (ope[i][0] == 3) {//modify for(j = ope[i][1]; j <= n; j += j & -j) Newadd(root[j], root[j], 1, id, w[ope[i][1]], -1); w[ope[i][1]] = ope[i][3]; for(j = ope[i][1]; j <= n; j += j & -j) Newadd(root[j], root[j], 1, id, w[ope[i][1]], 1); } else if (ope[i][0] == 4) {//prev setself(ope[i][1] - 1); setself(ope[i][2]); int less = getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]); setself(ope[i][1] - 1); setself(ope[i][2]); printf("%d\n", rank_weigh[getkth(1, id, less, ope[i][1] - 1, ope[i][2])]); } else {//succ setself(ope[i][1] - 1); setself(ope[i][2]); int less = getless(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]); setself(ope[i][1] - 1); setself(ope[i][2]); int num = getnum(1, id, ope[i][3], ope[i][1] - 1, ope[i][2]); setself(ope[i][1] - 1); setself(ope[i][2]); printf("%d\n", rank_weigh[getkth(1, id, less + num + 1, ope[i][1] - 1, ope[i][2])]); } } return 0; }