ZOJ2112 Dynamic Rankings(树套树:树状数组套主席树)
写法一:
树状维护区间,内部主席树(动态开点)维护值域;
树状数组的结点i代表第i个版本的权值线段树并且其是建立在原区间 [ i - lowbit(i) + 1 , i ] ;
通过树状数组对结点(每个节点都是一个线段树)求和,就能得到一个完整版本的主席树;
由于树状数组的特殊性,这种写法不需要保存历史版本,直接通过树状向上更新即可;
但也正是这种写法不能保存历史版本,达不到树链的共用,在内存开销上比较大;
#include<bits/stdc++.h> #pragma GCC optimize(2) using namespace std; typedef long long LL; typedef pair<int,int> pii; typedef pair<double,double> pdd; const int N=6e4+5; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-9; const long double pi=acos(-1.0L); #define ls (i<<1) #define rs (i<<1|1) #define fi first #define se second #define pb push_back #define mk make_pair #define mem(a,b) memset(a,b,sizeof(a)) LL read() { LL x=0,t=1; char ch; while(!isdigit(ch=getchar())) if(ch=='-') t=-1; while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } return x*t; } struct node{ int l,r,v; }c[N*40]; int root[N],t[N],a[N],L[N],R[N],K[N],cmd[N],len,tot,n,m,cnt; int qx[N],qy[N],tx,ty; void update(int l,int r,int &now,int pos,int d) { if(now==0) now=++cnt;//没有保存历史版本,如果之前结点存在,也就没有必要再开一个新的节点了; c[now].v+=d; if(l==r) return ; int mid=l+r>>1; if(pos<=mid) update(l,mid,c[now].l,pos,d); else update(mid+1,r,c[now].r,pos,d); } inline int lowbit(int x) { return x&(-x); } void add(int x,int y) { for(int i=x;i<=n;i+=lowbit(i)) update(1,len,root[i],a[x],y); } int query(int l,int r,int k) { if(l==r) return l; int mid=l+r>>1; int sum=0; for(int i=1;i<=ty;i++) sum+=c[c[qy[i]].l].v; for(int i=1;i<=tx;i++) sum-=c[c[qx[i]].l].v; if(k<=sum) { for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].l; for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].l; return query(l,mid,k); } else { for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].r; for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].r; return query(mid+1,r,k-sum); } } int main() { int T=read(); while(T--) { for(int i=1;i<=cnt;i++) root[i]=c[i].l=c[i].r=c[i].v=0; cnt=tx=ty=0; tot=n=read(),m=read(); for(int i=1;i<=n;i++) t[i]=a[i]=read(); for(int i=1;i<=m;i++) { char ch[2]; int x,y,z; scanf("%s",ch); cmd[i]=ch[0]; if(ch[0]=='Q') L[i]=read(),R[i]=read(),K[i]=read(); else L[i]=read(),t[++tot]=R[i]=read(); } sort(t+1,t+tot+1); len=unique(t+1,t+tot+1)-t-1; //for(int i=1;i<=len;i++) printf("%d%c",t[i],i==len?'\n':' '); for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+len+1,a[i])-t; for(int i=1;i<=n;i++) add(i,1); for(int i=1;i<=m;i++) { if(cmd[i]=='Q') { tx=ty=0; for(int j=L[i]-1;j;j-=lowbit(j)) qx[++tx]=root[j]; for(int j=R[i];j;j-=lowbit(j)) qy[++ty]=root[j]; printf("%d\n",t[query(1,len,K[i])]); } else { add(L[i],-1); a[L[i]]=lower_bound(t+1,t+len+1,R[i])-t; add(L[i],1); } } } return 0; }
写法二:
在写法一的基础上进行改进:写法一的树状是维护原区间以及修改部分的主席树,(n+m)lognlogn 的内存量相当大,
而第二种写法中,直接对原区间建立静态的主席树,而树状只维护主席树被修改的部分,原理同方法一,每次询问则同时查询静态主席树和树状的被修改部分,
如此一来空间复杂度就能优化到 O(nlogn+mlognlogn) 了;
#include<bits/stdc++.h> #pragma GCC optimize(2) using namespace std; typedef long long LL; typedef pair<int,int> pii; typedef pair<double,double> pdd; const int N=5e4+5; const int M=1e4+5; const int inf=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-9; const long double pi=acos(-1.0L); #define ls (i<<1) #define rs (i<<1|1) #define fi first #define se second #define pb push_back #define mk make_pair #define mem(a,b) memset(a,b,sizeof(a)) LL read() { LL x=0,t=1; char ch; while(!isdigit(ch=getchar())) if(ch=='-') t=-1; while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } return x*t; } struct node{int l,r,s;}c[N*40]; int root[N],rooc[N];//root 主席树的根,rooc 树状的根 int tmp[N+M],a[N],cnt,len,tot,n,m; int qx[M],qy[M],tx,ty,L[M],R[M],K[M],cmd[N]; inline int lowbit(int x) { return x&(-x); } void update(int l,int r,int pre,int &now,int pos,int v) { now=++cnt,c[now]=c[pre],c[now].s+=v; //这里建立静态主席树,需要保存历史版本,如果想在树状上节约空间,可以加一个flag if(l==r) return ; int mid=l+r>>1; if(pos<=mid) update(l,mid,c[pre].l,c[now].l,pos,v); else update(mid+1,r,c[pre].r,c[now].r,pos,v); } void add(int x,int y) { int d=lower_bound(tmp+1,tmp+len+1,a[x])-tmp; for(int i=x;i<=n;i+=lowbit(i)) update(1,len,rooc[i],rooc[i],d,y); } int query(int l,int r,int x,int y,int k) { if(l==r) return l; int mid=l+r>>1; int sum=c[c[y].l].s-c[c[x].l].s; for(int i=1;i<=tx;i++) sum-=c[c[qx[i]].l].s; for(int i=1;i<=ty;i++) sum+=c[c[qy[i]].l].s; if(k<=sum) { for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].l; for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].l; return query(l,mid,c[x].l,c[y].l,k); } else { for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].r; for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].r; return query(mid+1,r,c[x].r,c[y].r,k-sum); } } int main() { int T=read(); while(T--) { tot=n=read(),m=read(); for(int i=1;i<=n;i++) tmp[i]=a[i]=read(); for(int i=1;i<=m;i++) { char ch[2]; scanf("%s",ch); cmd[i]=ch[0]=='Q'; if(cmd[i]==1) L[i]=read(),R[i]=read(),K[i]=read(); else L[i]=read(),tmp[++tot]=R[i]=read(); } sort(tmp+1,tmp+tot+1); len=unique(tmp+1,tmp+tot+1)-tmp-1; for(int i=1;i<=n;i++) { int x=lower_bound(tmp+1,tmp+len+1,a[i])-tmp; update(1,len,root[i-1],root[i],x,1); //printf("cnt = %d\n",cnt); } for(int i=1;i<=m;i++) { if(cmd[i]) { tx=ty=0; for(int j=L[i]-1;j;j-=lowbit(j)) qx[++tx]=rooc[j]; for(int j=R[i];j;j-=lowbit(j)) qy[++ty]=rooc[j]; printf("%d\n",tmp[query(1,len,root[L[i]-1],root[R[i]],K[i])]); } else { add(L[i],-1); a[L[i]]=R[i]; add(L[i],1); } } for(int i=1;i<=cnt;i++) root[i]=rooc[i]=0,c[i]=c[0]; cnt=0; } return 0; }
内存节约版update:(树状不保存历史版本,静态主席树保存历史版本)
void update(int l,int r,int pre,int &now,int pos,int v,int flag)//flag=1,主席树;flag=0,树状数组 { if(flag||!flag&&now==0) now=++cnt,c[now]=c[pre]; c[now].s+=v; if(l==r) return ; int mid=l+r>>1; if(pos<=mid) update(l,mid,c[pre].l,c[now].l,pos,v,flag); else update(mid+1,r,c[pre].r,c[now].r,pos,v,flag); }