参考大佬博客:「黑历史」浅谈权值线段树到主席树 (想直接看主席树的话就跳到中间部分开始看)
总述:
(以求区间第k小为例)值域线段树维护一个单调递增的值域的数的出现次数。主席树将原序列的下标看做时间戳,这里还应用了前缀和、前缀可减性的思想。把求原序列区间[l,r]上的答案(这里为第k小的数)的问题,转化为将第r个版本与第l-1个版本的可持久化值域线段树相减后得到新树,再从新树上求全局答案的问题。但实际操作中并不用真的做一次线段树合并(相减),只需在这两棵线段树上从根开始同时走,记第r个版本与第l-1个版本的线段树当前节点的键值(这里为当前节点代表值域中的数的出现次数)分别为v、u,只需将k与u-v相比后 得出答案/递归就好(若向右递归,别忘k-=u-v)。
题解口胡:
先离散化。然后按总述说的做。
ac代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 5 #define qiumid (l+r>>1) 6 7 using namespace std; 8 9 const int N=2e5+5; 10 11 int n,tre[N*23],cnt,hed[N],lson[N*23],rson[N*23],m,nn; 12 int dic[N]; 13 14 struct node{ 15 int num,ord,def; 16 }ai[N]; 17 18 inline int read()//N 19 { 20 int x=0; 21 bool f=0; 22 char ch=getchar(); 23 while(!isdigit(ch)) 24 f|=ch=='-',ch=getchar(); 25 while(isdigit(ch)) 26 x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 27 return f?-x:x; 28 } 29 30 inline bool cmp1(const node &a,const node &b) 31 { 32 return a.num<b.num; 33 } 34 35 inline bool cmp2(const node &a,const node &b) 36 { 37 return a.ord<b.ord; 38 } 39 40 void build(int t,int l,int r) 41 { 42 if(l==r) 43 return; 44 int mid=qiumid; 45 lson[t]=++cnt; 46 build(cnt,l,mid); 47 rson[t]=++cnt; 48 build(cnt,mid+1,r); 49 } 50 51 void insert(int u,int t,int l,int r,int w) 52 { 53 if(l==r) 54 { 55 tre[u]=tre[t]+1; 56 return; 57 } 58 int mid=qiumid; 59 if(w<=mid) 60 { 61 rson[u]=rson[t]; 62 lson[u]=++cnt; 63 insert(cnt,lson[t],l,mid,w); 64 } 65 else 66 { 67 lson[u]=lson[t]; 68 rson[u]=++cnt; 69 insert(cnt,rson[t],mid+1,r,w); 70 } 71 tre[u]=tre[lson[u]]+tre[rson[u]]; 72 } 73 74 int fin(int u,int v,int l,int r,int k) 75 { 76 if(l==r) 77 return l; 78 int kk; 79 if((kk=tre[lson[v]]-tre[lson[u]])>=k) 80 return fin(lson[u],lson[v],l,qiumid,k); 81 else 82 return fin(rson[u],rson[v],qiumid+1,r,k-kk); 83 } 84 85 int main() 86 { 87 // freopen(".in","r",stdin); 88 // freopen(".out","w",stdout); 89 n=read();m=read(); 90 for(int i=1;i<=n;++i) 91 { 92 ai[i].num=read(); 93 ai[i].ord=i; 94 } 95 sort(ai+1,ai+1+n,cmp1); 96 ai[1].def=nn=1; 97 dic[1]=ai[1].num; 98 for(int i=2;i<=n;++i) 99 { 100 if(ai[i].num==ai[i-1].num) 101 ai[i].def=nn; 102 else 103 { 104 ai[i].def=++nn; 105 dic[nn]=ai[i].num; 106 } 107 } 108 sort(ai+1,ai+1+n,cmp2); 109 hed[0]=cnt=1; 110 build(1,1,nn); 111 for(int i=1;i<=n;++i) 112 { 113 hed[i]=++cnt; 114 insert(cnt,hed[i-1],1,nn,ai[i].def); 115 } 116 int ll,rr,k; 117 for(int i=1;i<=m;++i) 118 { 119 ll=read(),rr=read(),k=read(); 120 printf("%d ",dic[fin(hed[ll-1],hed[rr],1,nn,k)]); 121 } 122 return 0; 123 }
应用:
静态区间(即无修改,有修改请去隔壁树套树)第k大