首先借主席树发明人的一段话:
..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> < 想法是对原序列的每一个前缀[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(); } }
一: 先分析空间复杂度:
每加入一个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