弃疗.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 保存下节点就好了