fhq treap 之 可持久化

弃疗.jpg

这个破代码整整改了我不知道n次 交了n次 满满的玄学气息....

先给你们看看我的假持久化版本 拿了96分 被卡了一个点...(应该的)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define N 500005
using namespace std;
int n,val[N],rnd[N],son[N][2],size[N],sz,rt[N];
inline int newnode(int x)
{
    ++sz;
    size[sz]=1;
    val[sz]=x;
    rnd[sz]=rand()*rand();
    return sz; 
}
inline void update(int x)
{
    size[x]=size[son[x][0]]+size[son[x][1]]+1;	//加上自己别忘了 
}
void split(int &x,int &y,int k,int now)	//x,左子树的根(权值较小的),y,右子树的根,now,现在的节点 
{
    //作为递归的边界 包含两种情况 1:第一次split 必须把x,y赋为0;2:到了叶子节点时 此时其实只需要一个return 不过为了方便才那样写 
    if(!now)	x=y=0;
    else
    {
        if(val[now]<=k)	//当这个点的权值小于k 我们直接从右子树开始split 把这个点的左子树分到x里面去 
        {
            x=now;	//保存操作 等回溯上来时会通过地址符号传给x 
            split(son[now][1],y,k,son[now][1]); 
        }
        else
        {
            y=now;
            split(x,son[now][0],k,son[now][0]);
        }
        update(now);
    }
}
int merge(int x,int y)	//保证y子树权值大于x子树 
{
    //最终目的是建出一颗rand值从小到大 val值符合left_son<father<right_son 
    if(x==0||y==0)	return x+y;
    if(rnd[x]<rnd[y])
    {
        son[x][1]=merge(son[x][1],y);	//保留左子树 你想如果merge在左子树上 二叉搜索树的平衡性就被破坏了 
        update(x);
        return x;
    }
    else
    {
        son[y][0]=merge(x,son[y][0]);	//保留右子树 反过来把x接在y的右子树上 
        update(y);
        return y;
    }
}
int kth(int now,int rank)
{
    while(1)
    {
        if(size[son[now][0]]>=rank)
        {
            now=son[now][0];
        }
        else if(size[son[now][0]]+1==rank)	//由于是儿子 要加上自己 
        {
            return now;
        }
        else
        {
        	rank-=size[son[now][0]]+1;
            now=son[now][1];	//小心顺序。。。 
        }
    }
    
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL); 
    //奥义 增加cin cout速度 趋近于scanf 
    srand(19260817666);
    cin>>n;
  
    for(int i=1;i<=n;i++)
    {
    	int v,opt,a,b,x,y,z;
        cin>>v>>opt>>a;
        rt[i]=rt[v];
        if(opt==1)//insert
        {
            split(x,y,a,rt[i]); //分成一颗权值小于a 一颗大于a的 最终递归完了会通过地址符号改变x,y的值
            rt[i]=merge(merge(x,newnode(a)),y); 
        }
        if(opt==2)//delete
        {
            split(x,z,a,rt[i]);	//按照a分裂成两半 
            split(x,y,a-1,x);	//在x子树里 把所有值等于a都分出来 
            y=merge(son[y][0],son[y][1]);	//由于不止一个a 
            rt[i]=merge(merge(x,y),z);
        } 
        if(opt==3)	//a's kth
        {
            split(x,y,a-1,rt[i]);
            cout<<size[x]+1<<endl;
            rt[i]=merge(x,y);	//别忘再合并回来 
        }
        if(opt==4)	//kth
        {
            cout<<val[kth(rt[i],a)]<<endl;
        }
        if(opt==5)	//a's before
        {
            split(x,y,a-1,rt[i]);
            cout<<val[kth(x,size[x])]<<endl;
            rt[i]=merge(x,y);
        }
        if(opt==6)	//a's after
        {
            split(x,y,a,rt[i]);
            cout<<val[kth(y,1)]<<endl;
            rt[i]=merge(x,y);
        } 
    }
    return 0;
}

这个代码有严重的漏洞。。。就是merge split的时候是直接修改原版本信息的 破坏了原来的版本 不能实现可持久化

正解应该是这样的...     

#include<bits/stdc++.h>
#define N 23000005
using namespace std;
int n,val[N],rnd[N],son[N][2],size[N],sz,rt[500005];
inline int newnode(int x)
{
    ++sz;
    size[sz]=1;
    val[sz]=x;
    rnd[sz]=rand()*rand();
    return sz; 
}
inline void copy(int ne,int old)
{
	size[ne]=size[old];
	val[ne]=val[old];
	son[ne][0]=son[old][0];
	son[ne][1]=son[old][1];
	rnd[ne]=rnd[old];
	return;
}
inline void update(int x)
{
    size[x]=size[son[x][0]]+size[son[x][1]]+1;	//加上自己别忘了 
}
void split(int &x,int &y,int k,int now)	//x,左子树的根(权值较小的),y,右子树的根,now,现在的节点 
{
    //作为递归的边界 包含两种情况 1:第一次split 必须把x,y赋为0;2:到了叶子节点时 此时其实只需要一个return 不过为了方便才那样写 
    if(!now)	x=y=0;
    else
    {
        if(val[now]<=k)	//当这个点的权值小于k 我们直接从右子树开始split 把这个点的左子树分到x里面去 
        {
            x=++sz;	//保存操作 等回溯上来时会通过地址符号传给x 
        	copy(x,now);
			split(son[x][1],y,k,son[x][1]); 
			update(x);
        }
        else
        {
            y=++sz;
            copy(y,now);
            split(x,son[y][0],k,son[y][0]);
        	update(y);
		}
    }
}
int merge(int x,int y)	//保证y子树权值大于x子树 
{
    //最终目的是建出一颗rand值从小到大 val值符合left_son<father<right_son 
    if(x==0||y==0)	return x+y;
    if(rnd[x]<rnd[y])
    {
    	int p=++sz;
    	copy(p,x);
        son[p][1]=merge(son[p][1],y);	//保留左子树 你想如果merge在左子树上 二叉搜索树的平衡性就被破坏了 
        update(p);
        return p;
    }
    else
    {
    	int p=++sz;
    	copy(p,y);
        son[p][0]=merge(x,son[p][0]);	//保留右子树 反过来把x接在y的右子树上 
        update(p);
        return p;
    }
}
int kth(int now,int rank)
{
    while(1)
    {
        if(size[son[now][0]]>=rank)
        {
            now=son[now][0];
        }
        else if(size[son[now][0]]+1==rank)	//由于是儿子 要加上自己 
        {
            return now;
        }
        else
        {
        	rank-=size[son[now][0]]+1;
            now=son[now][1];	//小心顺序。。。 
        }
    }
    
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL); 
    //奥义 增加cin cout速度 趋近于scanf 
    srand((unsigned)time(NULL));
    cin>>n;
  
    for(int i=1;i<=n;i++)
    {
    	int v,opt,a,b,x,y,z;
        cin>>v>>opt>>a;
        rt[i]=rt[v];
        if(opt==1)//insert
        {
            split(x,y,a,rt[i]); //分成一颗权值小于a 一颗大于a的 最终递归完了会通过地址符号改变x,y的值
            rt[i]=merge(merge(x,newnode(a)),y); 
        }
        if(opt==2)//delete
        {
            split(x,z,a,rt[i]);	//按照a分裂成两半 
            split(x,y,a-1,x);	//在x子树里 把所有值等于a都分出来 
            y=merge(son[y][0],son[y][1]);	//由于不止一个a 
            rt[i]=merge(merge(x,y),z);
        } 
        if(opt==3)	//a's kth
        {
            split(x,y,a-1,rt[i]);
            cout<<size[x]+1<<endl;
            rt[i]=merge(x,y);	//别忘再合并回来 
        }
        if(opt==4)	//kth
        {
            cout<<val[kth(rt[i],a)]<<endl;
        }
        if(opt==5)	//a's before
        {
            split(x,y,a-1,rt[i]);
            cout<<val[kth(x,size[x])]<<endl;
            rt[i]=merge(x,y);
        }
        if(opt==6)	//a's after
        {
            split(x,y,a,rt[i]);
            cout<<val[kth(y,1)]<<endl;
            rt[i]=merge(x,y);
        } 
    }
    return 0;
}
唔...每次split merge 保存下节点就好了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值