【资料】个人对主席树算法的理解

来源:互联网 发布:windows 8.1怎么激活 编辑:程序博客网 时间:2024/06/05 02:12

首先借主席树发明人的一段话:

..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> <想法是对原序列的每一个前缀[1..i]建立出一颗线段树维护值域上每个数的出现次数,然后发现这样的树是可以减的,然后就没有然后了

转载请注明出处,谢谢。
http://blog.csdn.net/sprintfwater/article/details/9162041
首先定义:

a[MAXN],a2[MAXN];struct node{node *ch[2];int siz;node(){ch[0]=ch[1]=NULL;siz=0;}node(node *_ch0,node *_ch1,int _siz):siz(_siz){ch[0]=_ch0,ch[1]=_ch1;}void update(){if (ch[0]) siz+=ch[0]->siz;if (ch[1]) siz+=ch[1]->siz;}}*null=new node(),*root[MAXN]={NULL};

自己理解:


1:前提条件:有N个数字,size个不同的数字。
2:用a[N]存储原来的数字,a2[size]存储排好序的数字;
3:也就是每一个点root【i】都维护着size大小的线段树,而该线段树维护的信息为:在a[1] 到a[i]数字集合中,size种数字分别出现的次数。
4:root[i] - > ch[0] 保存的是排好序,前size/2种数字分别出现的次数,root[i] - > ch[1] 保存的是排好序,后size/2种数字分别出现的次数;


值得注意的是,并不是每个root【i】都必须重新开辟size * log2(size)的空间,比如当新加入的a【i】非常小,那么root[i] ->ch[1]后size/2种数字分别出现的次数
相对于root[i - 1] -> ch[1]是不会变。所以可以只用有孩子指针指向root[i - 1] -> ch[1]所指的节点就可以共用。当新加入的a【i】非常大的时候,那么ch【0】就可以共用。


建树的关键代码:

void make_node(node *&y,node *&x,int l,int r,int t){if (x==NULL) x=null;y=new node();int m=(l+r)>>1;if (l==r)//已经到达叶子节点,{*y=*x;y->siz++;return;}if (t<=a2[m]) {make_node(y->ch[0],x->ch[0],l,m,t);y->ch[1]=x->ch[1];y->update();}else{make_node(y->ch[1],x->ch[1],m+1,r,t);y->ch[0]=x->ch[0];y->update();}}

为了防止new爆T,那可以先用数组存好。




一: 先分析空间复杂度:
每加入一个a【i】时,就会增加log2(size)个新节点,也就是每一层会产生一个新节点。所以空间复杂度为:N * log2(size)


二:当查看一个一个区间中的 第K大值时
在第一层时:
1.先查看这段区间在前size/2个的出现次数是否大于k,如果是,那么第k个,肯定在前size/2中,否则在后size/2中,
那么第k大,就得减去前size/2个出现的次数之和,变为在后size/2种数字中求第(k - 前size/2个出现的次数之和)大
2. 而前size/2个的出现次数可以直接相减可得,比如要看区间L到R,前size/2种数字出现的次数之和,
等于(root[R] - > ch[0] -> size) - (root[L - 1] -> ch[0] - >size)


3.往下层走,只不过就是size变为了size/2,递归下去就可以了。


很容易看出查询一次的时间复杂度为log2(size);


关键代码如下:

void find(node *&x1,node *&x2,int l,int r,int k){if (x1==NULL) x1=null;if (x2==NULL) x2=null;if (l==r) {printf("%d\n",a2[l]);return;}int m=(l+r)>>1;int ls=0;if (x2->ch[0]) ls+=x2->ch[0]->siz;if (x1->ch[0]) ls-=x1->ch[0]->siz;if (ls>=k) find(x1->ch[0],x2->ch[0],l,m,k);else find(x1->ch[1],x2->ch[1],m+1,r,k-ls); }

下面是网上转载的主席树完整代码:
http://blog.csdn.net/sprintfwater/article/details/9156571

0 0
原创粉丝点击