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

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

https://www.luogu.org/problemnew/show/P3834

主席树支持历史查询,空间复杂度为O(nlogn),需要动态开点

本题用一个类似于前缀和的思想,离散化之后

用主席树维护每一个前缀的“桶”数组

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 #define MAXN 200020
 6 int n,m,a[MAXN],b[MAXN],lc[MAXN<<5],rc[MAXN<<5],root[MAXN<<5],num,sum[MAXN<<5];
 7 inline int read(){
 8     int x=0,f=1; char c=getchar();
 9     while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
10     while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
11     return x*f;
12 }
13 int build(int l,int r){
14     int rt=++num;
15     sum[rt]=0;
16     int mid=(l+r)>>1;
17     if(l<r){
18         lc[rt]=build(l,mid);  //记录左儿子
19         rc[rt]=build(mid+1,r); //记录右儿子
20     }
21     return rt;
22 }
23 int update(int last,int l,int r,int p){
24     int rt=++num;
25     lc[rt]=lc[last]; rc[rt]=rc[last]; sum[rt]=sum[last]+1;  //新开结点
26     int mid=(l+r)>>1;
27     if(l<r){  
28         if(p<=mid) lc[rt]=update(lc[last],l,mid,p);
29         else  rc[rt]=update(rc[last],mid+1,r,p);
30     }
31     return rt;
32 }
33 int query(int u,int v,int l,int r,int k){
34     if(l>=r) return l;
35     int t=sum[lc[v]]-sum[lc[u]];          //原序列区间[u,v]中值在[l,r]之间的数的个数
36     int mid=(l+r)>>1;
37     if(k<=t) return query(lc[u],lc[v],l,mid,k);
38     else return query(rc[u],rc[v],mid+1,r,k-t);
39 }
40 int main()
41 {
42     scanf("%d%d",&n,&m);
43     for(int i=1;i<=n;i++){
44         a[i]=read(); b[i]=a[i];
45     }
46     sort(b+1,b+1+n);
47     int h=unique(b+1,b+1+n)-b-1;
48     root[0]=build(1,h);
49     for(int i=1;i<=n;i++){
50         int t=lower_bound(b+1,b+1+h,a[i])-b;
51         root[i]=update(root[i-1],1,h,t);
52     }
53     int x,y,k;
54     while(m--){
55         x=read(); y=read(); k=read();
56         int t=query(root[x-1],root[y],1,h,k);
57         printf("%d\n",b[t]);
58     }
59     return 0;
60 }

猜你喜欢

转载自www.cnblogs.com/yjkhhh/p/9217668.html