Day 7
上午数据结构串讲。
Question:难的数据结构简单的题和简单的数据结构难的题你选哪个?
我选了前面的…
摸鱼。
非旋treap学习
不用旋转的平衡树!!!
可以持久化!!!
代码超短!!!
神级数据结构
非旋的treap与旋转treap的相同点就是每个点都有一个随机pri值,依旧保持堆的性质。
不一样的地方就是非旋treap的操作不再是rotate,而是split和merge。
split有两种,一种是按权值分裂,一种是按排名分裂,根据题目的需要来判断用哪种。
按权值分裂就是对于当前点now,如果now的权值小于等于分裂权值k,那么now的左边一定也都<=k,我们将now的右边和y相连。反之同理。
void split(int now,int k,int &x,int &y){
if(!now){
x=y=0;
}
else{
if(v[now]<=k){
x=now;
split(ch[now][1], k, ch[now][1], y);
}
else{
y=now;
split(ch[now][0], k, x, ch[now][0]);
}
push_up(now);
}
}
merge就是将根为x和根为y的两颗树进行合并。
int merge(int x,int y){
if(!x || !y){
return x+y;//必定有一个为空
}
if(p[x]<p[y]){
ch[x][1]=merge(ch[x][1], y);
push_up(x);
return x;
}
else{
ch[y][0]=merge(x, ch[y][0]);
push_up(y);
return y;
}
//merge返回最终树的根
}
对于插入操作,我们新建一个点,再将这个树关于该点权值split为根为x和y的两棵树,再合并x,新点,再和y合并。
int create(int x){
v[++Size]=x;//v是节点权值
p[Size]=rand();//p是随机权重
siz[Size]=1;//siz是子树节点数
return Size;//Size表示一共的点个数
}
split(root, num, x, y);
root=merge(merge(x, create(num)), y);
删除操作就是将树关于删除权值 p split成两颗根分别为x和y的树。
再将x关于权值 p-1 split成两颗根分别为x和z的树。
我们现在可以肯定节点z的权值必为p,那我们丢掉它就好了,把z的左子树和右子树合并,再将根赋给z,我们就删去了一个节点。最后将x和z合并为x,再将x和y合并,我们就完成了删除操作。
split(root, num, x, y);
split(x, num-1, x, z);
z=merge(ch[z][0], ch[z][1]);
root=merge(merge(x, z), y);
持久化操作
对于每个版本,我们记下来该版本的根,对于新版本就 新建/删除 节点就行了。
删除节点不是真的把它删掉,只是将父子关系改变一下就行了。
每个版本都记着自己的信息。
注意在split和merge中也要新建状态,不然会被卡掉。
#include <cstdio>
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
const int N = 50000001;
int n,siz;
int ch[N][2],root[N],s[N],v[N],p[N];
void push_up(int x){
s[x]=1+s[ch[x][0]]+s[ch[x][1]];
}
int create(int x){
s[++siz]=1;
v[siz]=x;
p[siz]=rand();
return siz;
}
void cpy(int pre,int now){//复制状态
ch[now][1]=ch[pre][1];
ch[now][0]=ch[pre][0];
s[now]=s[pre];
v[now]=v[pre];
p[now]=p[pre];
}
int merge(int x,int y){
if(!x || !y){
return x+y;
}
if(p[x]<p[y]){
int tmp=++siz;
cpy(x, tmp);//创建新的状态
ch[tmp][1]=merge(ch[tmp][1], y);
push_up(tmp);
return tmp;
}
else{
int tmp=++siz;
cpy(y, tmp);
ch[tmp][0]=merge(x, ch[tmp][0]);
push_up(tmp);
return tmp;
}
}
void split(int now,int k,int &x,int &y){
if(!now){
x=y=0;
}
else{
if(v[now]<=k){
x=++siz;
cpy(now, x);
split(ch[x][1], k, ch[x][1], y);
push_up(x);
}
else{
y=++siz;
cpy(now, y);
split(ch[y][0], k, x, ch[y][0]);
push_up(y);
}
}
}
int kth(int now,int x){
while(1){
if(s[ch[now][0]]>=x){
now=ch[now][0];
}
else {
if(s[ch[now][0]]+1==x){
return now;
}
else{
x-=s[ch[now][0]]+1;
now=ch[now][1];
}
}
}
}
int main(){
srand(time(0));
scanf("%d",&n);
int x=0,y=0,z=0;
for(int i=1; i<=n; i++){
int t,opt,num;
scanf("%d%d%d",&t,&opt,&num);
root[i]=root[t];
if(opt==1){
split(root[i], num, x, y);
root[i]=merge(merge(x, create(num)), y);
}
else if(opt==2){
split(root[i], num, x, z);
split(x, num-1, x, y);
y=merge(ch[y][0], ch[y][1]);
root[i]=merge(merge(x, y), z);
}
else if(opt==3){
split(root[i], num-1, x, y);
printf("%d\n", s[x]+1);
root[i]=merge(x, y);
}
else if(opt==4){
printf("%d\n",v[kth(root[i], num)]);
}
else if(opt==5){
split(root[i], num-1, x, y);
printf("%d\n",v[kth(x, s[x])]);
root[i]=merge(x, y);
}
else if(opt==6){
split(root[i], num, x, y);
printf("%d\n",v[kth(y, 1)]);
root[i]=merge(x, y);
}
}
return 0;
}