主席树(可持久化线段树)学习笔记

主席树学习笔记

1.理解:

一种可持久化数据结构,本质上是可持久化线段树,若加上前缀和思想便为主席树。

其思想为:单点修改时,只有被修改的点到根的一条链上的点会发生改变,建一个新根,添一条新链,并与不改变的点相连。以此为基础,建立权值线段树,记录每个值(可以预先离散)出现的次数,将序列中的点看作增加某个值出现的次数。

开新点时与动态开点线段树相似。

2.功能:

求静态/动态的区间第k大。

首先,对于区间[1,r]的第k大,我们可以在权值线段树每个节点x:(代表区间[a,b])处判断节点x的左儿子y所代表区间[a,(a+b)/2]中值的出现次数与k的大小情况,如果出现次数sum[y]>=k,第k大数定在[a,(a+b)/2]区间中,否则在区间[(a+b)/2+1,b]中,最后a=b时的a即是第k大数。

当区间[1,r]推广到[l,r]时,只有出现次数由sum[r号版本]变为sum[r号版本]-sum[(l-1)号版本],其它的就一样喽。

那如何从静态推广到动态呢?

先思考暴力算法,若修改第x个数,将x号版本到n号版本的权值线段树都修改一遍。

这岂不是又是区间[x,n]的修改操作?

我们考虑用最简单的区间修改数据结构——树状数组来维护。

注:需要注意,我们需要建一棵0号版本的初始树(满的线段树),否则后继版本无法继承,且l=1时,无l-1号版本。

3.代码:

<1>.静态主席树:

例题:洛谷P3834 [模板]可持久化线段树 1(主席树)

 1 #include<bits/stdc++.h>
 2 #define re register
 3 using namespace std;
 4 const int N=2e5+6;
 5 int n,m,rt[N],son[N<<5][2],sum[N<<5],id=0,a[N],b[N],p,t,t1,t2,t3;
 6 inline int read()
 7 {
 8     int T=0,F=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
10     while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
11     return F*T;
12 }
13 void build(int l,int r,int &x)
14 {
15     if(!x) x=++id;
16     sum[x]=0;
17     if(l==r) return;
18     int mid=((l+r)>>1);
19     build(l,mid,son[x][0]),build(mid+1,r,son[x][1]);
20 }
21 void update(int l,int r,int &x,int y,int q)
22 {
23     if(!x) x=++id;
24     sum[x]=sum[y]+1;
25     if(l==r) return;
26     int mid=((l+r)>>1);
27     if(q<=mid) update(l,mid,son[x][0],son[y][0],q),son[x][1]=son[y][1];
28     else update(mid+1,r,son[x][1],son[y][1],q),son[x][0]=son[y][0];
29 }
30 int kth(int l,int r,int x,int y,int q)
31 {
32     while(l<=r)
33     {
34         if(l==r) return l;
35         int mid=((l+r)>>1); t=sum[son[x][0]]-sum[son[y][0]];
36         if(q>t) q-=t,l=mid+1,x=son[x][1],y=son[y][1];
37         else r=mid,x=son[x][0],y=son[y][0];
38     }
39 }
40 int main()
41 {
42     n=read(),p=read();
43     for(re int i=1;i<=n;++i) a[i]=read(),b[i]=a[i];
44     sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1;//离散化 
45     rt[0]=0; build(1,m,rt[0]);//建初始树 
46     for(re int i=1;i<=n;++i)
47     {
48         t=lower_bound(b+1,b+m+1,a[i])-b;//t为离散后的编号 
49         update(1,m,rt[i],rt[i-1],t);//下一个历史版本的修改 
50     }
51     while(p--)
52     {
53         t1=read(),t2=read(),t3=read();
54         printf("%d\n",b[kth(1,m,rt[t2],rt[t1-1],t3)]);
55     }
56     return 0;
57 }

 

<2>.动态主席树:

例题:洛谷P2617 Dynamic Rankings

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100006;
 4 int n,m,id=0,rt[N<<9],sum[N<<9],son[N<<9][2],a[N<<1],b[N<<1],tot1,tot2,t,num,o,e[106],f[106],num1,num2;
 5 char c[12];
 6 struct xd
 7 {
 8     int t2,t3,t4;
 9     char t1;
10 }wew[N];
11 inline int read()
12 {
13     int T=0,F=1; char ch=getchar();
14     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
15     while(ch>='0'&&ch<='9') T=(T<<1)+(T<<3)+(ch-48),ch=getchar();
16     return F*T; 
17 } 
18 void update(int l,int r,int &x,int q,int p)
19 {
20     if(!x) x=++id;
21     sum[x]+=p;
22     if(l==r) return;
23     int mid=((l+r)>>1);
24     if(q<=mid) update(l,mid,son[x][0],q,p);
25     else update(mid+1,r,son[x][1],q,p);
26 }
27 int query(int l,int r,int q)
28 {
29     while(l<=r)
30     {
31         if(l==r) return l;
32         tot1=tot2=0;
33         for(int i=1;i<=num1;++i) tot1+=sum[son[e[i]][0]];
34         for(int i=1;i<=num2;++i) tot2+=sum[son[f[i]][0]];
35         int mid=((l+r)>>1);
36         if(q<=tot1-tot2)
37         {
38             r=mid;
39             for(int i=1;i<=num1;++i) e[i]=son[e[i]][0];
40             for(int i=1;i<=num2;++i) f[i]=son[f[i]][0];
41         }
42         else
43         {
44             q=q-tot1+tot2,l=mid+1;
45             for(int i=1;i<=num1;++i) e[i]=son[e[i]][1];
46             for(int i=1;i<=num2;++i) f[i]=son[f[i]][1];
47         }
48     } 
49 }
50 inline int lowbit(int x){return x&(-x);}
51 void add(int u,int v,int w){
52     for(int i=u;i<=n;i+=lowbit(i)) update(1,num,rt[i],v,w);
53 }
54 int getsum(int r,int l,int q)
55 {
56     num1=num2=0;
57     for(int i=r;i;i-=lowbit(i)) e[++num1]=rt[i];
58     for(int i=l;i;i-=lowbit(i)) f[++num2]=rt[i];
59     //e、f数组里是需要修改的节点的编号 
60     return query(1,num,q);
61 }
62 int main()
63 {
64     n=read(),m=read(); o=n;
65     for(int i=1;i<=n;++i) b[i]=read(),a[i]=b[i];
66     for(int i=1;i<=m;++i)
67     {
68         scanf("%s",c),wew[i].t1=c[0],wew[i].t2=read(),wew[i].t3=read();
69         if(c[0]=='C') b[++o]=wew[i].t3;
70         else wew[i].t4=read();
71     }
72     //存入输入和修改,一并离散 
73     sort(b+1,b+o+1);
74     num=unique(b+1,b+o+1)-b-1;
75     for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+num+1,a[i])-b,add(i,a[i],1);
76     //权值离散 
77     for(int i=1;i<=m;++i)
78     {
79         if(wew[i].t1=='Q') printf("%d\n",b[getsum(wew[i].t3,wew[i].t2-1,wew[i].t4)]);
80         else add(wew[i].t2,a[wew[i].t2],-1),a[wew[i].t2]=lower_bound(b+1,b+num+1,wew[i].t3)-b,add(wew[i].t2,a[wew[i].t2],1);
81     } 
82     return 0;
83 }

 

<3>.可持久化线段树:

例题:洛谷P3919 [模板]可持久化数组(可持久化线段树/平衡树)

 

 1 #include<bits/stdc++.h>
 2 #define re register
 3 using namespace std;
 4 const int N=1e6+6;
 5 int n,m,son[N<<5][2],id=0,num[N<<5],opt,t1,t2,t3,a[N],rt[N];
 6 inline int read()
 7 {
 8     int T=0,F=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
10     while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
11     return F*T;
12 }
13 void build(int l,int r,int &x)
14 {
15     if(!x) x=++id;
16     if(l==r){num[x]=a[l]; return;}
17     int mid=((l+r)>>1);
18     build(l,mid,son[x][0]),build(mid+1,r,son[x][1]);
19 }
20 void update(int l,int r,int &x,int y,int q)
21 {
22     if(!x) x=++id;
23     if(l==r){num[x]=t3; return;}
24     int mid=((l+r)>>1);
25     if(q<=mid) update(l,mid,son[x][0],son[y][0],q),son[x][1]=son[y][1];
26     else update(mid+1,r,son[x][1],son[y][1],q),son[x][0]=son[y][0];
27 }
28 int query(int l,int r,int x,int q)
29 {
30     while(l<=r)
31     {
32         if(l==r) return num[x];
33         int mid=((l+r)>>1);
34         if(q<=mid) r=mid,x=son[x][0];
35         else l=mid+1,x=son[x][1];
36     }
37 }
38 int main()
39 {
40     n=read(),m=read();
41     for(re int i=1;i<=n;++i) a[i]=read();
42     build(1,n,rt[0]);
43     for(re int i=1;i<=m;++i)
44     {
45         t1=read(),opt=read(),t2=read();
46         if(opt==1) t3=read(),update(1,n,rt[i],rt[t1],t2);
47         else rt[i]=++id,printf("%d\n",query(1,n,rt[t1],t2)),son[id][0]=son[rt[t1]][0],son[id][1]=son[rt[t1]][1];
48     }
49     return 0;
50 }

 

posted @ 2019-08-01 22:41  lsoi_ljk123  阅读(165)  评论(0编辑  收藏  举报