现在把主席树的原理给弄清楚了,从i=1开始,每次新插入一个数,就为他建一棵线段树(当然第一次i=0的时候是建一棵空树),线段树里面保存的是1-i的树的位置情况
简单来说,如果有m个树,则每棵线段树都是范围为1-m的,至于1-i没有m个那就先让它空着不管,我只负责1-i里面的数的位置情况插入到线段树里面,左孩子是大小k<=size/2的,右孩子是k>size的,size是线段树的深入层次而定,一开始就是m的,而不用每次都为1-i每个树插入值(不仅耗时,空间也耗不起),我只考虑当前这个Ai,至于1到i-1,已经在T[i-1]里面存了,我如果当前孩子是往左走的,那我复用T[i-1]的右孩子即可,反过来也是一样的,这样,每次建树 最多logN,最多也只建了logN个节点,所有总节点数目为N*logN,在N为不超过千万的范围内,是可以承受的。。。不过这道题为什么要*40才能过。。我也没搞清,好像觉得不用开这么大,但我试了下不开久会WA。
因为主席树每颗线段树结构相同并且还高度复用,所以他的一个很奇妙的特性就是他支持加减操作,一般就是用他的减操作,要求区间A到B的,则用T[j] 和T[i-1]的左支相减即可得到当前这个区间里线段树左边存的是多大的,右边存的是多大的,用pos代进去即可,往左走 往右走,把整个二分走完即可得到答案
树状数组套线段树我暂时还没完全弄清,反正为了记录修改,每次都像之前插入值一样新开了树,而且要用离线,把要修改的那些值预先存进去
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N =60000+10; const int maxn=N*40; int tot; int T[N],A[N],t[N],s[N]; int lson[maxn],rson[maxn],c[maxn]; int used[N]; int n,q,m; struct node { int kind,l,r,k; }Q[10010]; void init() { sort(t+1,t+1+m); m=unique(t+1,t+1+m)-t-1; } int build(int l,int r) { int rt=tot++; c[rt]=0; if (l>=r) return rt; int mid=(l+r)>>1; lson[rt]=build(l,mid); rson[rt]=build(mid+1,r); return rt; } int lowbit(int x) { return x&(-x); } int sum(int x) { int ret=0; while (x>0) { ret+=c[lson[used[x]]]; x-=lowbit(x); } return ret; } int query(int L,int R,int k) { int lrt=T[L-1]; int rrt=T[R]; int l=1,r=m; for (int i=L-1;i;i-=lowbit(i)){ used[i]=s[i]; } for (int i=R;i;i-=lowbit(i)){ used[i]=s[i]; } while (l<r) { int mid=(l+r)>>1; int tmp=sum(R)-sum(L-1)+c[lson[rrt]]-c[lson[lrt]]; if (tmp>=k){ r=mid; for (int i=L-1;i;i-=lowbit(i)){ used[i]=lson[used[i]]; } for (int i=R;i;i-=lowbit(i)){ used[i]=lson[used[i]]; } lrt=lson[lrt]; rrt=lson[rrt]; } else{ l=mid+1; k-=tmp; for (int i=L-1;i;i-=lowbit(i)){ used[i]=rson[used[i]]; } for (int i=R;i;i-=lowbit(i)){ used[i]=rson[used[i]]; } lrt=rson[lrt]; rrt=rson[rrt]; } } return l; } int inserts(int rt,int pos,int val) { int newrt=tot++,tmp=newrt; int l=1,r=m; c[newrt]=c[rt]+val; while (l<r) { int mid=(l+r)>>1; if (pos<=mid){ lson[newrt]=tot++; rson[newrt]=rson[rt]; rt=lson[rt]; newrt=lson[newrt]; r=mid; } else{ rson[newrt]=tot++; lson[newrt]=lson[rt]; rt=rson[rt]; newrt=rson[newrt]; l=mid+1; } c[newrt]=c[rt]+val; } return tmp; } void add(int loc,int p,int val) { while (loc<=n) { s[loc]=inserts(s[loc],p,val); loc+=lowbit(loc); } } int main() { char op[5]; int kase,a,b,k; scanf("%d",&kase); while (kase--) { tot=0; scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) scanf("%d",&A[i]),t[i]=A[i]; m=n; for (int i=0;i<q;i++){ scanf("%s",op); if (op[0]=='Q'){ scanf("%d%d%d",&a,&b,&k); Q[i]=(node){0,a,b,k}; } else{ scanf("%d%d",&a,&b); Q[i]=(node){1,a,b,0}; t[++m]=b; } } init(); T[0]=build(1,m); for (int i=1;i<=n;i++){ int pos=lower_bound(t+1,t+m+1,A[i])-t; T[i]=inserts(T[i-1],pos,1); } for (int i=0;i<=n;i++) s[i]=T[0]; for (int i=0;i<q;i++) { int a,b,k; if (Q[i].kind==0){ a=Q[i].l;b=Q[i].r;k=Q[i].k; int ans=query(a,b,k); printf("%d ",t[ans]); } else{ a=Q[i].l; b=Q[i].r; int pos; pos=lower_bound(t+1,t+1+m,A[a])-t; add(a,pos,-1); pos=lower_bound(t+1,t+1+m,b)-t; add(a,pos,1); A[a]=b; } } } return 0; }