主席树之初见

•何为主席树

                图1 

主席树的构造如图,以前序遍历的方式编号,叶子表示1到n

因为叶子是1到n,就有了左子树总是小于右子树的性质

除叶子外的节点记录的是区间sum代表这个节点的叶子有多少个数

如图 区间[2,2]有1个数,区间[3,3]有1个数

所以区间[1,2]有1个数,区间[3,4]有2个数,区间[1,4]有3个数

•性质

  • 左子树总是小于右子树

因为从左到右叶子是1到n

     设树的sum=x+y,左子树sum=x,右子树sum=y

所以在找第k小的时候,由于左子树小于右子树,所以前x小肯定在左子树上,第x+1到第x+y小在右子树上

     当k<=x时,就在左子树上找,当k>x时在右子树上找,

     在右子树上找时,由于右子树第一个是第x+1小,所以找第k小也就是找右子树的第k-x小

  • 区间可减性

 由于sum记录的是个数,假设现在是第L棵树,经过n次操作变成第R棵树

 由于每次操作都是修改一次,所以R的左子树sum-L的左子树sum=从L到R的左子树的操作数

 所以求[L,R]区间的第k小,可以利用R的左子树sum-L的左子树sum即为[L,R]内左子树上的数的个数

•例题

洛谷3919

这个题是可持久化的题,并非完全的主席树

这个题的叶子的值不是1到n,而是根据输入而定

也没有用到上述的性质

利用这个题来学习可持久化,为主席树做铺垫

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+5;
 4 struct Node
 5 {
 6     int l,r;
 7     int num;///num为值,这里与主席树不同
 8     Node(){num=0;}
 9 }seg[maxn*20];
10 int root[maxn],a[maxn];
11 int cnt=0;
12 void build(int l,int r,int &rt)///以前序遍历的方式建树
13 {
14     rt=++cnt;///rt为编号
15     if(l==r)
16     {
17         seg[rt].num=a[l];
18         return ;
19     }
20     int mid=(r+l)>>1;
21     build(l,mid,seg[rt].l);
22     build(mid+1,r,seg[rt].r);
23 }
24 
25 void update(int l,int r,int &rt,int pos,int y)
26 {
27     seg[++cnt]=seg[rt];///复制前一棵树
28     rt=cnt;///处理所新建的这棵树
29     if(l==r)
30     {
31         seg[rt].num=y;
32         return ;
33     }
34     int mid=(r+l)>>1;
35     if(pos<=mid) update(l,mid,seg[rt].l,pos,y);
36     else update(mid+1,r,seg[rt].r,pos,y);
37 }
38 
39 int query(int l,int r,int rt,int pos)
40 {
41     if(l==r) return seg[rt].num;
42     int mid=(r+l)>>1;
43     if(pos<=mid) return query(l,mid,seg[rt].l,pos);
44     else return query(mid+1,r,seg[rt].r,pos);
45 }
46 
47 int main()
48 {
49     ios::sync_with_stdio(0);
50     int n,m;
51     cin>>n>>m;
52     for(int i=1;i<=n;i++)
53         cin>>a[i];
54     build(1,n,root[0]);
55     int pre,op,x,y;
56     for(int i=1;i<=m;i++)
57     {
58         cin>>pre>>op>>x;
59         if(op==1)
60         {
61             cin>>y;
62             root[i]=root[pre];
63             update(1,n,root[i],x,y);
64         }
65         else
66         {
67             cout<<query(1,n,root[pre],x)<<endl;
68             root[i]=root[pre];
69         }
70     }
71 }
View Code

poj2104

主席树的模板题,用到了上述的性质

由于刚开始没有值,我就没有建树

其实做模板的话可以建一个空树

由于叶子是从1到n的,所以对于给出的数一般要离散化一下,使得对应1到n

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 #define ll long long
 5 const int maxn=1e5+5;
 6 int cnt,root[maxn];///每棵树的根
 7 int index[maxn];///按值大小排序后的序号
 8 struct node
 9 {
10     int l,r;
11     int sum;
12     node(){sum=0;}
13 }seg[maxn*40];
14 struct value
15 {
16     int v;
17     int id;
18 }V[maxn];
19 bool cmp(value a,value b)
20 {
21     return a.v<b.v;
22 }
23 
24 void init()
25 {
26     cnt=0;
27     seg[0].l=0,seg[0].r=0;
28     root[0]=0;
29 }
30 
31 void build(int l,int r,int &rt)///以前序遍历的方式建树
32 {
33     rt=++cnt;///rt为编号
34     if(l==r)
35         return ;
36     int mid=(r+l)>>1;
37     build(l,mid,seg[rt].l);
38     build(mid+1,r,seg[rt].r);
39 }
40 
41 void update(int l,int r,int &rt,int pos)
42 {
43     seg[++cnt]=seg[rt];///复制前一棵树
44     rt=cnt;///因为对新树进行操作,是rt为新树
45     seg[rt].sum++;///进行操作了,个数+1
46 
47     if(l==r) return ;
48     int mid=(l+r)>>1;
49     if(mid>=pos)
50         update(l,mid,seg[rt].l,pos);
51     else
52         update(mid+1,r,seg[rt].r,pos);
53 }
54 
55 int query(int L,int R,int l,int r,int k)
56 {
57     int d=seg[seg[R].l].sum-seg[seg[L].l].sum;///利用区间可减性,左子树上一共有d个数
58     if(l==r) return l;
59     int mid=(l+r)>>1;
60     if(d>=k)///利用 左子树<右子树 前d个在左子树上,其他的在右子树上
61         return query(seg[L].l,seg[R].l,l,mid,k);
62     else///右子树第一个是第d+1个 找第k个也就是找右子树的第k-d个
63         return query(seg[L].r,seg[R].r,mid+1,r,k-d);
64 
65 }
66 
67 int main()
68 {
69     int n,m;
70     cin>>n>>m;
71     for(int i=1;i<=n;i++)
72     {
73         cin>>V[i].v;
74         V[i].id=i;
75     }
76     ///离散化
77     sort(V+1,V+1+n,cmp);
78     for(int i=1;i<=n;i++)
79         index[V[i].id]=i;
80     init();
81 //    build(1,n,root[0]);
82     for(int i=1;i<=n;i++)
83     {
84         root[i]=root[i-1];///复制前一个树
85         update(1,n,root[i],index[i]);
86     }
87     int L,R,k;
88     for(int i=1;i<=m;i++)
89     {
90         cin>>L>>R>>k;
91         cout<<V[query(root[L-1],root[R],1,n,k)].v<<endl;
92     }
93 }
View Code

猜你喜欢

转载自www.cnblogs.com/MMMinoz/p/11440109.html
今日推荐