动态区间询问kth,单点修改。
区间用线段树分解,线段树上每条线段存一颗平衡树。
不能直接得到kth,但是利用val和比val小的个数之间的单调性,二分值。log^3N。
修改则是一次logN*logN。
总体是Nlog^2N+Mlog^3N。
一个值可以对应多个名次。每次查询严格小于val的个数。
把之前的Treap的值域加了一个vs表示值的出现次数,这样就可以支持重复的val了,并可以统计出值出现次数。
这样每个值的名次就变成一个区间了。
复杂度更低的做法:树状数组套主席树,还不太会。
#include<bits/stdc++.h> using namespace std; #define PS push const int maxn = 5e4+5; const int LgN = 17, maxnds = maxn*LgN; namespace Treap { int r[maxnds],s[maxnds],v[maxnds],ch[maxnds][2],vs[maxnds]; const int nil = 0, chsz = sizeof(ch[0]); #define CLRch(x) memset(ch[x],0,chsz); stack<int> meos; void Treap_init(){ for(int i = maxnds; --i; ){ meos.PS(i); } s[nil] = 0; } inline int newNode(int val){ int i = meos.top(); meos.pop(); r[i] = rand(); v[i] = val; vs[i] = s[i] = 1; CLRch(i) return i; } inline void delt(int i) { meos.PS(i); } #define lch ch[o][0] #define rch ch[o][1] inline void mt(int o){ s[o] = s[lch] + s[rch] + vs[o]; } inline int cmp(int a,int b){ return a == b?-1:(a>b?0:1); } inline void rot(int &o,int d){ int k = ch[o][d^1]; ch[o][d^1] = ch[k][d]; ch[k][d] = o; mt(o); mt(k); o = k; } int qval; void inst(int &o){ if(!o){ o = newNode(qval); }else { int d = cmp(v[o],qval); if(!~d) { s[o]++; vs[o]++; return; } inst(ch[o][d]); if(r[ch[o][d]] > r[o]) rot(o,d^1); else mt(o); } } void rmov(int &o){ int d = cmp(v[o],qval); if(!~d){ if(vs[o] > 1) { s[o]--; vs[o]--; return; } if(!lch){ delt(o); o = rch; }else if(!rch){ delt(o); o = lch; }else { int d2 = r[lch] > r[rch] ? 1 : 0; rot(o,d2); rmov(ch[o][d2]); } }else{ rmov(ch[o][d]); } if(o) mt(o); } void clrTree(int &o){ if(lch) clrTree(lch); if(rch) clrTree(rch); delt(o); o = nil; } int Finded; int Order(int &o){ if(!o) return 0; int d = cmp(v[o],qval); if(!~d){ Finded += vs[o]; return s[lch]; }else{ return (d?s[lch]+vs[o]:0) + Order(ch[o][d]); } } } using namespace Treap; int rt[maxn<<2]; int a[maxn]; int n; #define para int o = 1, int l = 1,int r = n #define lo (o<<1) #define ro (o<<1|1) #define TEMP int mid = (l+r)>>1; #define lsn lo, l, mid #define rsn ro, mid+1, r #define insd ql<=l&&r<=qr int ql,qr,val; void build(para) { if(rt[o]) clrTree(rt[o]); for(int i = l; i <= r; i++){ qval = a[i]; inst(rt[o]); } if(l == r) return; else { TEMP build(lsn); build(rsn); } } int query(para) { if(insd){ return Order(rt[o]); }else { TEMP int re = 0; if(ql<=mid) re += query(lsn); if(qr>mid) re += query(rsn); return re; } } int qpos; void Change(para) { qval = a[qpos]; rmov(rt[o]); qval = val; inst(rt[o]); if(l < r){ TEMP if(qpos <= mid) Change(lsn); else Change(rsn); } } //#define LOCAL int main() { #ifdef LOCAL freopen("data.txt","r",stdin); #endif int T; scanf("%d",&T); Treap_init(); while(T--){ int m; scanf("%d%d",&n,&m); int low = 1e9, high = 0; for(int i = 1; i <= n; i++) { scanf("%d",a+i); low = min(a[i],low); high = max(a[i],high); } build(); while(m--){ char op[2]; int x,y; scanf("%s%d%d",op,&x,&y); if(*op == 'Q'){ ql = x; qr = y; int k; scanf("%d",&k); int L = low,R = high; while(L<R){ int M = (L+R+1)>>1; qval = M; Finded = 0; int re = query(); if(k <= re) R = M-1; else if(k > re+Finded) L = M+1; else { L = M; break; } } printf("%d ",L); }else { qpos = x; val = y; Change(); a[qpos] = y; low = min(y,low); high = max(y,high); } } } return 0; }