【模板】可持久化权值线段树(主席树)
洛谷3834
主席树入门题,静态区间第k小
权值线段树:一棵线段树的叶子tree[L=R]节点记录序列中满足a[i]=L=R的数的个数,非叶子节点记录儿子的sum之和; 这样我们就可以快速地求出整个序列的第K小(或第K大)
为了能够查询区间的第K小,我们在序列1~n的每个位置i建立一棵权值线段树,那么对于区间[X,Y],tree[Y][l=r=k].sum-tree[X-1][l=r=k].sum即为这个区间中满足a[i]=k的数的个数。
这用到了前缀和的思想。通过这种方式我们可以快速查询区间第k小。
1 #include<cstdio> 2 #include<algorithm> 3 #define ls(x) (a[x].l) 4 #define rs(x) (a[x].r) 5 #define mid ((l+r)>>1) 6 using namespace std; 7 const int maxn=200010; 8 int n,m,N,x,y,z,tot,b[maxn],c[maxn],root[maxn]; 9 struct tree{int l,r,sum;}a[5000000]; 10 void read(int &k){ 11 k=0; int f=1; char c=getchar(); 12 while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); 13 while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar(); 14 k*=f; 15 } 16 void add(int &u,int l,int r,int pos){ 17 a[++tot]=a[u]; a[u=tot].sum++; 18 if(l<r) if(pos<=mid) add(ls(u),l,mid,pos); else add(rs(u),mid+1,r,pos); 19 } 20 int query(int x,int y,int l,int r,int k){ 21 if(l==r) return l; 22 if(a[ls(y)].sum-a[ls(x)].sum>=k) return query(ls(x),ls(y),l,mid,k); 23 else return query(rs(x),rs(y),mid+1,r,k-a[ls(y)].sum+a[ls(x)].sum); 24 } 25 int main(){ 26 read(n); read(m); 27 for(int i=1;i<=n;i++) read(b[i]),c[i]=b[i]; 28 sort(c+1,c+1+n); N=unique(c+1,c+n+1)-c-1; 29 for(int i=1;i<=n;i++) b[i]=lower_bound(c+1,c+N+1,b[i])-c; 30 for(int i=1;i<=n;i++) add(root[i]=root[i-1],1,N,b[i]); 31 while(m--){ 32 read(x); read(y); read(z); 33 printf("%d\n",c[query(root[x-1],root[y],1,N,z)]); 34 } 35 return 0; 36 }