主席树算法解析
Posted Danzel♂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树算法解析相关的知识,希望对你有一定的参考价值。
主席树
感想:
这个主席树我还是学了接近一周了,虽然思想懂得比较快,但是一直比较浮躁,所以
一直都没有静下来去看代码,但是在一周的慢慢消化中,我还是懂了主席树的思想和代码
,我也看了其他大牛的博客,都写的很好,所以我的这一篇都是比较水的
主席树是线段树的一种分支,可以解决一道题中的历史遗留问题或者第几大
例如:1,2,5,3,2,2,3,4
求区间 【3,7】中的第三小的数字,我们可以很容易的看出是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.接着在这棵树上不断判断mid和k的关系,找到第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:第一次发这么长的帖子,可能会有很多瑕疵,希望诸位大佬指出
以上是关于主席树算法解析的主要内容,如果未能解决你的问题,请参考以下文章