主席树算法解析

主席树

感想:

这个主席树我还是学了接近一周了,虽然思想懂得比较快,但是一直比较浮躁,所以

一直都没有静下来去看代码,但是在一周的慢慢消化中,我还是懂了主席树的思想和代码

,我也看了其他大牛的博客,都写的很好,所以我的这一篇都是比较水的

 

主席树是线段树的一种分支,可以解决一道题中的历史遗留问题或者第几大

例如:12532234

求区间 37】中的第三小的数字,我们可以很容易的看出是3

然后这样一种类型其实就是主席树的模板题了

推荐模板题:poj2104  

Poj2761

Hdu2665

 

算法:我的语言形容能力不好,我就尽量用图来表达吧

定义的量:

T[],sum[],R[],L[],a[],b[],d,tot,rt

T[]:第几棵树的含有的数字个数

Sum[i]:节点i含有的数字个数

L[i]:节点i的左边界

R[i]:节点i的右边界

d:去重后数组剩下的个数

Tot:最后的节点总数

1.首先我们建立一棵空树,里面每一个节点都是0

 

2.然后向里面加点,我们先挨着建树,没有优化,每一个节点存的是区间里的数字个数

3.用一个优化的图,这种方式不会爆内存(因为画完的话占的面积太大,所以我就举例子加入第8个点时,即树7向树8转换)

树8就是除去蓝色斜杠杠掉的节点

 

4.假如我们要求区间[3,7]3小,我们就用树7去减树2再来看(l,r区间求法是:t[r]-t[l-1]

 

5.接着在这棵树上不断判断midk的关系,找到第k

 

这就是这个主席树了,我们接着来看代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #define maxn 100005
 8 using namespace std;
 9 
10 int sum[maxn<<5],L[maxn<<5],R[maxn<<5],t[maxn<<5];
11 int tot,d,x,m,n,a[maxn],b[maxn];
12 
13 int build(int l,int r)//在这个问题里,这一步实际意义不大,就是建立空树 
14 {//                      主要是累加tot 
15     int rt=(++tot);
16     int m=(l+r)>>1;
17     sum[rt]=0;
18     if(l<r)
19     {
20         build(l,m);
21         build(m+1,r);
22     }
23     return rt;
24 }
25 
26 int update(int pre,int l,int r,int x)
27 {
28     int rt=(++tot);
29     L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
30     if(l<r)
31     {
32         int m=(l+r)>>1;
33         if(x<=m)L[rt]=update(L[pre],l,m,x);//需要变更的链 
34         else R[rt]=update(R[pre],m+1,r,x);
35     }
36     return rt;
37 }
38 
39 int query(int u,int v,int l,int r,int k)
40 {
41     if(l>=r)return l;
42     int m=(l+r)>>1;
43     int num=sum[L[v]]-sum[L[u]];//两棵树对应节点相减 
44     if(num>=k){//左边已经到了第num名 
45         return query(L[u],L[v],l,m,k);
46     }else return query(R[u],R[v],m+1,r,k-num);
47     
48 }
49 
50 int main()
51 {
52     scanf("%d%d",&n,&m);
53     for(int i=1;i<=n;i++) 
54     {
55         scanf("%d",&a[i]);
56         b[i]=a[i];    
57     }
58     sort(b+1,b+n+1); 
59     int d=unique(b+1,b+n+1)-(b+1);//去重后的数字个数
60     t[0]=build(1,d);
61     for(int i=1;i<=n;i++)
62     {
63         int x=lower_bound(b+1,b+d+1,a[i])-b;
64         t[i]=update(t[i-1],1,d,x); 
65     }
66     for(int j=1;j<=m;j++)
67     {
68         int l,r,k;
69         scanf("%d%d%d",&l,&r,&k);
70         int y=query(t[l-1],t[r],1,d,k);//求l,r区间就是树r减去l-1 
71         printf("%d\n",b[y]);
72     }
73     
74 } 

 

PS:第一次发这么长的帖子,可能会有很多瑕疵,希望诸位大佬指出

 

posted @ 2017-07-26 11:05  Danzel♂  阅读(452)  评论(1编辑  收藏  举报