主席樹,聽說由fotile大佬因不會劃分樹而發明。因爲大佬名字縮寫爲HJT而被Seter大佬命名爲主席樹。c++
主要用於查詢區間第k值。git
首先離散化,對每一個節點i建一棵[1,i]的權值線段樹。這裏用到了前綴和思想而且知足可加減性。但這會MLE。而後咱們發現,對於每一個點的線段樹,它和前面那個節點的線段樹有且僅有logn個節點是不一樣的,所以咱們能夠借用前一個節點的線段樹,僅僅新建logn個節點。這個思想成爲了主席樹和全部可持久化數據結構的思想基礎。數組
1 int n,m; 2 int a[maxn]; //初始數組 3 int lch[maxn],all; //離散化 4 struct Node { 5 int L,R,sum; //左兒子、右兒子編號和在此區間內的數的個數 6 } tree[maxn*16]; 7 int ins; 8 int root[maxn]; //每一個節點的線段樹的根
整個序列直接建樹有點困難(也多是我太菜了),考慮依次插入每一個數。數據結構
若是到葉子節點就在前一個點的基礎上更新並直接返回,不然爲相應的兒子分配新節點。ide
1 void insert(int now,int l,int r,int x,int pla) { //now爲前一個點的當前編號,pla爲分配節點編號 2 if(l==r) { 3 tree[pla].sum=tree[now].sum+1; //在前一個點的基礎上更新節點 4 return; 5 } 6 int mid=l+r>>1; 7 if(x>mid) { 8 tree[pla].L=tree[now].L; //複製左兒子 9 insert(tree[now].R,mid+1,r,x,(tree[pla].R=++ins)); //新建右兒子並向右走 10 } else { 11 tree[pla].R=tree[now].R; //複製右兒子 12 insert(tree[now].L,l,mid,x,(tree[pla].L=++ins)); //新建左兒子並向左走 13 } 14 update(pla); 15 }
利用前綴和思想,傳入l-1和r,相減便可。ui
1 int query(int l,int r,int z,int y,int k) { 2 if(l==r) return l; //葉子節點就是答案 3 int mid=l+r>>1; 4 return (k>tree[tree[y].L].sum-tree[tree[z].L].sum)? 5 query(mid+1,r,tree[z].R,tree[y].R,k-(tree[tree[y].L].sum-tree[tree[z].L].sum)): //左邊太少,往右走 6 query(l,mid,tree[z].L,tree[y].L,k); //左邊太多,往左走 7 }
建樹:O(logn)spa
單次查詢:和線段樹同樣O(logn)3d
因爲每一個值僅出現logn次,有n個值,所以空間複雜度爲O(nlogn)code
洛谷P3834 【模板】可持久化線段樹 1(主席樹)blog
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x7fffffff 4 #define ME 0x7f 5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout) 6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c)) 7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c)) 8 #define fel(i,a) for(register int i=h[a];i;i=ne[i]) 9 #define ll long long 10 #define MEM(a,b) memset(a,b,sizeof(a)) 11 #define maxn (200000+10) 12 int n,m; 13 int a[maxn]; 14 int lch[maxn],all; 15 struct Node{ 16 int L,R,sum; 17 }tree[maxn*16]; 18 int ins; 19 int root[maxn]; 20 template<class T> 21 inline T read(T &n){ 22 n=0;int t=1;double x=10;char ch; 23 for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-1:n=ch-'0'; 24 for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-'0'; 25 if(ch=='.') for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-'0')/x,x*=10; 26 return (n*=t); 27 } 28 template<class T> 29 T write(T n){ 30 if(n<0) putchar('-'),n=-n; 31 if(n>=10) write(n/10);putchar(n%10+'0');return n; 32 } 33 template<class T> 34 T writeln(T n){write(n);putchar('\n');return n;} 35 void update(int x){tree[x].sum=tree[tree[x].L].sum+tree[tree[x].R].sum;} 36 void insert(int now,int l,int r,int x,int pla){ 37 if(l==r){tree[pla].sum=tree[now].sum+1;return;}int mid=l+r>>1; 38 if(x>mid){tree[pla].L=tree[now].L;insert(tree[now].R,mid+1,r,x,(tree[pla].R=++ins));} 39 else{tree[pla].R=tree[now].R;insert(tree[now].L,l,mid,x,(tree[pla].L=++ins));}update(pla); 40 } 41 int query(int l,int r,int z,int y,int k){ 42 if(l==r) return l;int mid=l+r>>1; 43 return (k>tree[tree[y].L].sum-tree[tree[z].L].sum)? 44 query(mid+1,r,tree[z].R,tree[y].R,k-(tree[tree[y].L].sum-tree[tree[z].L].sum)): 45 query(l,mid,tree[z].L,tree[y].L,k); 46 } 47 int main(){ 48 read(n);read(m); 49 fui(i,1,n,1) lch[i]=read(a[i]); 50 sort(lch+1,lch+n+1);all=unique(lch+1,lch+n+1)-lch-1; 51 fui(i,1,n,1){ 52 int p=lower_bound(lch+1,lch+all+1,a[i])-lch; 53 insert(root[i-1],1,all,p,(root[i]=++ins)); 54 } 55 fui(i,1,m,1){ 56 int l,r,k; 57 read(l);read(r);read(k); 58 writeln(lch[query(1,all,root[l-1],root[r],k)]); 59 } 60 return 0; 61 }