转载自【学习笔记】主席树 最详细的主席树(不修改,待修改) BZOJ 1901
主席树的思想,个人感觉在于线段树动态开点和前缀和。详细的过程,上面的两个博客感觉讲得挺好,就不重复,直接上两个入门题。
P3567 [POI2014]KUR-Couriers
区间第K大。
直接以权值线段树来建主席树,然后根据前缀和,就可以知道对应的范围的主席树内有多少数,然后找第K大即可。
#include<bits/stdc++.h> using namespace std; const int N=5e5+11; struct Tree{ int lson,rson,sum; }T[N*32]; int tn,a[N],root[N]; int build(int l,int r){ int cur=++tn,mid=(l+r)>>1; T[cur].sum=0; if(l==r) return cur; T[cur].lson=build(l,mid); T[cur].rson=build(mid+1,r); return cur; } int addT(int x,int l,int r,int p){ int cur=++tn,mid=(l+r)>>1; T[cur]=T[x]; T[cur].sum++; if(l==r) return cur; if(p<=mid) T[cur].lson=addT(T[x].lson,l,mid,p); else T[cur].rson=addT(T[x].rson,mid+1,r,p); return cur; } int query(int L,int R,int l,int r,int num){ if(l==r) return l; int mid=(l+r)>>1; int lsum=T[T[R].lson].sum-T[T[L].lson].sum; int rsum=T[T[R].rson].sum-T[T[L].rson].sum; if(lsum>=num) return query(T[L].lson,T[R].lson,l,mid,num); if(rsum>=num) return query(T[L].rson,T[R].rson,mid+1,r,num); return 0; } int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); root[0]=build(1,n); for(int i=1;i<=n;i++) root[i]=addT(root[i-1],1,n,a[i]); int l,r; while(m--){ scanf("%d%d",&l,&r); printf("%d ",query(root[l-1],root[r],1,n,(r-l+1)/2+1)); } } return 0; }
D-query
区间内不同的数的个数。
这里把查询的区间离线下来,然后用线段树或者树状数组维护也可,思路便是记录每个数出现的上一个位置pos,然后从左往右遍历的时候,对于ai来说,i位置+1,pos[ai]位置-1,这样便使得查询的每个区间里的每个相同的数都只在最右边的位置被计算到,当每个碰到一个查询区间的右边界时,去查找答案即可。
那主席树的做法的话,不再像上题一样以权值线段树来建主席树,而是以区间来建主席树。思路的话也是类似线段树的做法,记录每个数出现的上一个位置pos,然后在新建一棵修改对i处+1的主席树前,先建一棵对pos[a[i]]处-1的主席树,当然,两者的顺序可以反过来。
#include<bits/stdc++.h> using namespace std; const int N=3e4+11,M=1e6+11; struct Tree{ int lson,rson,val; }T[N*32]; int tn,a[N],root[N],vis[M]; int build(int l,int r){ int cur=++tn,mid=(l+r)>>1; T[cur].val=0; T[cur].lson=T[cur].rson=0; if(l==r) return cur; T[cur].lson=build(l,mid); T[cur].rson=build(mid+1,r); return cur; } int addT(int x,int l,int r,int p,int val){ int cur=++tn,mid=(l+r)>>1; T[cur]=T[x]; T[cur].val=T[x].val+val; if(l==r) return cur; if(p<=mid) T[cur].lson=addT(T[x].lson,l,mid,p,val); else T[cur].rson=addT(T[x].rson,mid+1,r,p,val); return cur; } int query(int x,int l,int r,int ql){ if(l>=ql) return T[x].val; int ans=0,mid=(l+r)>>1; if(ql<=mid) ans+=query(T[x].lson,l,mid,ql); ans+=query(T[x].rson,mid+1,r,ql); return ans; } int main(){ int n; while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){ scanf("%d",&a[i]); vis[a[i]]=0; } tn=0; root[0]=build(1,n); for(int i=1;i<=n;i++){ int tempr=root[i-1]; if(vis[a[i]]) tempr=addT(tempr,1,n,vis[a[i]],-1); root[i]=addT(tempr,1,n,i,1); vis[a[i]]=i; } int m,l,r; scanf("%d",&m); while(m--){ scanf("%d%d",&l,&r); printf("%d ",query(root[r],1,n,l)); } } return 0; }