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;
}