主席樹摘要

介紹&主要用途

主席樹,聽說由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];                                                                                //每一個節點的線段樹的根
var

建樹

整個序列直接建樹有點困難(也多是我太菜了),考慮依次插入每一個數。數據結構

若是到葉子節點就在前一個點的基礎上更新並直接返回,不然爲相應的兒子分配新節點。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 }
insert

查詢區間[l,r]第k小

利用前綴和思想,傳入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 }
query

時空複雜度

時間複雜度

建樹: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 }
AC代碼
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息