【模板】可持久化权值线段树(主席树)

洛谷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
 2 #include
 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(lif(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 }
View Code

 

转载于:https://www.cnblogs.com/DriverLao/p/8024814.html

你可能感兴趣的:(【模板】可持久化权值线段树(主席树))