【模板】可持久化线段树 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 ",b[t]); 58 } 59 return 0; 60 }