zoukankan      html  css  js  c++  java
  • 题解【可持久化平衡树】

    [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 ]

  • 相关阅读:
    JS中利用正则表达式提取一个字符串中的子字符串的方法
    Xcode的环境变量列表
    在未安装Visual Studio 2012的服务器上使用MSBuild以文件系统方式发布ASP.NET MVC系统
    使Web API支持二级实体操作,兼对RESTFul风格API设计的疑惑。
    忽略大小写的字符串包含测试
    Entity Framework里不用查询直接更新的办法
    iOS里生成灰化(黑白)图像
    微信小程序开发调试工具
    微信小程序产品定位及功能介绍
    微信小程序DEMO初体验
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/12243250.html
Copyright © 2011-2022 走看看