jz集训 8.7

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章