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