题解【可持久化平衡树】

[Preface ]

莫得 Preface 。

[Description ]

【普通平衡树】的可持久化版本。

[Solution ]

我们知道 ( ext{fhq treap}) 是资瓷可持久化的。

重点就在于 ( ext{fhq treap}) 的核心操作 (split)(merge) 上,这两个操作改变了 ( ext{fhq treap}) 的形态。

那也就是说我们在 (split)(merge) 的时候,新建立节点,将原节点的信息赋给新节点,再进行操作,使得当前版本的 ( ext{fhq treap}) 不影响到之前版本的 ( ext{fhq treap}) ,就达到了可持久化的目的。

split

void split_v(int p,int val,int &x,int &y)
{
	if(!p)
		x=y=0;
	else
	{
		if(t[p].val<=val)
		{
			x=++tot,t[x]=t[p];
			split_v(t[x].rc,val,t[x].rc,y),upd(x);
		}
		else
		{
			y=++tot,t[y]=t[p];
			split_v(t[y].lc,val,x,t[y].lc),upd(y);
		}
	}
}

merge

int merge(int p,int q)
{
	if(!p||!q)
		return p^q;
	else
	{
		int now=++tot;
		if(t[p].dat>t[q].dat)
			t[now]=t[p],t[now].rc=merge(t[now].rc,q);
		else
			t[now]=t[q],t[now].lc=merge(p,t[now].lc);
		upd(now);
		return now;
	}
}

但实际上 (insert)(remove) 的时候,我们在 (split) 的过程中已经完成了新建节点这一步,就没必要在 (merge) 的时候再多新建节点而保留那两棵要合并的树了。

剩下的操作只要复读复读复读就行了,或者说按有旋 ( ext{treap}) 一样的方式做。

时空复杂度 (Θ(n log n))

[Code ]

#include<cstdio>
#include<cstdlib>

#define RI register int

using namespace std;

inline int read()
{
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
	while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
	return x*f;
}

const int N=500100,MLOGN=30001000;

const int INF=2147483647;

int m;

int tot,root[N];
struct treap{
	int lc,rc;
	int val,dat;
	int size;
}t[MLOGN];

int New(int val)
{
	tot++;
	t[tot].lc=t[tot].rc=0;
	t[tot].val=val,t[tot].dat=rand();
	t[tot].size=1;
	return tot; 
}

void upd(int p)
{
	t[p].size=t[t[p].lc].size+t[t[p].rc].size+1;
}

void split_v(int p,int val,int &x,int &y)
{
	if(!p)
		x=y=0;
	else
	{
		if(t[p].val<=val)
		{
			x=++tot,t[x]=t[p];
			split_v(t[x].rc,val,t[x].rc,y),upd(x);
		}
		else
		{
			y=++tot,t[y]=t[p];
			split_v(t[y].lc,val,x,t[y].lc),upd(y);
		}
	}
}

int merge(int p,int q)
{
	if(!p||!q)
		return p^q;
	if(t[p].dat>t[q].dat)
	{
		t[p].rc=merge(t[p].rc,q),upd(p);
		return p;
	}
	else
	{
		t[q].lc=merge(p,t[q].lc),upd(q);
		return q;
	}
}

int x,y,z;

void insert(int &rt,int val)
{
	split_v(rt,val-1,x,y);
	rt=New(val);
	rt=merge(x,rt);
	rt=merge(rt,y);
}

void remove(int &rt,int val)
{
	split_v(rt,val-1,x,y);
	split_v(y,val,y,z);
	y=merge(t[y].lc,t[y].rc);
	rt=merge(x,y);
	rt=merge(rt,z);
}

int GetValByRank(int p,int rank)
{
	if(!p)
		return INF;
	if(rank<=t[t[p].lc].size)
		return GetValByRank(t[p].lc,rank);
	if(rank==t[t[p].lc].size+1)
		return t[p].val;
	if(t[t[p].lc].size<rank)
		return GetValByRank(t[p].rc,rank-t[t[p].lc].size-1);
}

int GetRankByVal(int p,int val)
{
	if(!p)
		return 0;
	if(val<t[p].val)
		return GetRankByVal(t[p].lc,val);
	if(val==t[p].val)
		return t[t[p].lc].size;
	if(t[p].val<val)
		return t[t[p].lc].size+1+GetRankByVal(t[p].rc,val);
}

int GetPre(int rt,int val)
{
	int ans=-INF;
	int p=rt;
	while(p)
	{
		if(val==t[p].val)
		{
			if(t[p].lc>0)
			{
				p=t[p].lc;
				while(t[p].rc>0)p=t[p].rc;
				ans=t[p].val;
			}
			break;
		}
		if(t[p].val<val&&t[p].val>ans)ans=t[p].val;
		p=val<t[p].val?t[p].lc:t[p].rc;
	}
	return ans;
}

int GetNext(int rt,int val)
{
	int ans=INF;
	int p=rt;
	while(p)
	{
		if(val==t[p].val)
		{
			if(t[p].rc>0)
			{
				p=t[p].rc;
				while(t[p].lc>0)p=t[p].lc;
				ans=t[p].val;
			}
			break;
		}
		if(t[p].val>val&&t[p].val<ans)ans=t[p].val;
		p=val<t[p].val?t[p].lc:t[p].rc;
	}
	return ans;
}

int main()
{
	insert(root[0],-INF),insert(root[0],INF);

	m=read();

	for(RI i=1;i<=m;i++)
	{
		int ver=read(),type=read(),val=read();
		root[i]=root[ver];

		switch(type)
		{
			case 1:{
				insert(root[i],val);

				break;
			}

			case 2:{
				remove(root[i],val);

				break;
			}

			case 3:{
				printf("%d
",GetRankByVal(root[i],val));

				break;
			}

			case 4:{
				printf("%d
",GetValByRank(root[i],val+1));

				break;
			}

			case 5:{
				printf("%d
",GetPre(root[i],val));

				break;
			}

			case 6:{
				printf("%d
",GetNext(root[i],val));

				break;
			}
		}
	}

	return 0;
}

[Thanks for watching ]

原文地址:https://www.cnblogs.com/cjtcalc/p/12243250.html