个人对主席树算法的理解


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

..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> <
想法是对原序列的每一个前缀[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

你可能感兴趣的:(个人对主席树算法的理解)