luoguP3369[模板]普通平衡樹(Treap/SBT) 題解
阿新 • • 發佈:2018-04-05
names main getchar() clu 父親節 ble blank fine while
鏈接一下題目:luoguP3369[模板]普通平衡樹(Treap/SBT)
#include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #include<stack> #define lst long long #definerg register #define N 500050 #define Inf 2147483647 using namespace std; int use,root,tot;//操作數,根節點編號,樹中元素總和 struct T{ int cnt;//這個數相等的數的數量 int size;//這棵子樹上一共有幾個元素 int fa,v;//父親節點,當前點的權值 int ch[2];//左(0)右(1)孩子 }ljl[N];//平衡樹 inline int read() { rg int s=0,m=1;char ch=getchar(); while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar(); if(ch==‘-‘)m=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)s=(s<<3)+(s<<1)+ch-‘0‘,ch=getchar(); return s*m; } inline void Pushup(rg int now)//更新節點size的操作 { ljl[now].size=ljl[ljl[now].ch[0]].size+ljl[ljl[now].ch[1]].size+ljl[now].cnt;//當前節點的size是左孩子子樹的size加上右孩子子樹的size加上本節點相等的數的數量 } inline void rotate(rg int x)//把x往上轉 {//定義:x的相對位置為x屬於y的?孩子 rg int y=ljl[x].fa;//父親 rg int z=ljl[y].fa;//祖父 rg int k=ljl[y].ch[1]==x;//x的相對位置 ljl[z].ch[ljl[z].ch[1]==y]=x;//把x轉到y的位置上去 ljl[x].fa=z;//x的爸爸變成了z ljl[y].ch[k]=ljl[x].ch[k^1];//y的x的相對位置的那個孩子變成x的x的相對位置的另一個孩子 ljl[ljl[x].ch[k^1]].fa=y;//……的爸爸變成y ljl[x].ch[k^1]=y;//x的相對位置的另一個孩子變成y ljl[y].fa=x;//y的爸爸變成x Pushup(x),Pushup(y);//更新一下節點數量 } inline void splay(rg int x,rg int goal)//把x轉到goal下面,如果goal=0,那麽就是轉到根節點 { while(ljl[x].fa!=goal)//如果x的父親不是goal,目標沒有達成,就要繼續轉 { rg int y=ljl[x].fa;//父親 rg int z=ljl[y].fa;//祖父 if(z!=goal)//如果z存在的話 { (x==ljl[y].ch[0])^(y==ljl[z].ch[0])?rotate(x):rotate(y); //如果x和y分別是y和z的同一孩子,就把y往上轉 //如果x和y分別是y和z的不同孩子,就把x往上轉 } rotate(x);//最後一定會要把x在網上轉一次 } if(!goal)root=x;//更新根節點 } void Insert(rg int x)//插入x { rg int now=root,fa=0;//從根開始找,根的父親是0 while(ljl[now].v!=x&&now)//只要還沒有找到這個數字,且當前這個位置有數,就繼續找 { fa=now;//爸爸變成現在的節點 now=ljl[now].ch[x>ljl[now].v];//如果x比now大,就找now的右孩子,小則左孩子 } if(now)ljl[now].cnt++;//如果存值的位置存在,就直接在計數器上加1 else//否則 { now=++tot;//增加一個新位置 if(fa)ljl[fa].ch[x>ljl[fa].v]=now;//如果父親存在(我不是根),那我的父親的兒子是我 ljl[now].v=x;//權值 ljl[now].fa=fa;//父親 ljl[now].cnt=1;//計數器 ljl[now].size=1;//子樹大小 ljl[now].ch[0]=ljl[now].ch[1]=0;//沒有孩子 } splay(now,0);//把當前位置轉到根節點,以維持樹的平衡 } inline void find(rg int x)//找x的位置,把它轉到根節點,方便之後的計算 { rg int now=root;//從根開始找 if(!root)return;//如果是空樹,還找個屁 while(x!=ljl[now].v&&ljl[now].ch[x>ljl[now].v])//如果還沒有找到那個點,且我還有符合的兒子 now=ljl[now].ch[x>ljl[now].v];//就跳轉到我的兒子繼續找 splay(now,0);//轉到樹根去 } inline int Next(rg int x,rg int f)//找x的前驅(0)後繼(1) { find(x);//先找到x的位置,可能樹頂不是x,是和x值接近的那個元素 int now=root;//從根開始找 if(ljl[now].v>x&&f)return now;//如果大於x且我們要找後繼,那就是他了 if(ljl[now].v<x&&!f)return now;//如果小於x且我們要找前驅,那就是他了 now=ljl[now].ch[f];//那我們從符合條件的兒子開始跳 while(ljl[now].ch[f^1])now=ljl[now].ch[f^1];//不斷的往最優的方向跳 return now;//返回位置 } inline void Delete(rg int x)//刪掉x { rg int qq=Next(x,0);//找到前驅 rg int hj=Next(x,1);//找到後繼 splay(qq,0),splay(hj,qq);//把前驅轉到樹根,把後繼轉到前驅下面 int del=ljl[hj].ch[0];//那麽x就是後繼的左兒子 if(ljl[del].cnt>1)//如果x的計數器大於1 { ljl[del].cnt--;//讓x的計數器-- splay(del,0);//轉到樹根保持平衡 } else ljl[hj].ch[0]=0;//直接刪除x } inline int kth(rg int x)//找第x小的數 { rg int now=root;//從根開始找 if(ljl[now].size<x)return 0;//如果排名都超過總數了………… while(1)//嘿嘿,一直找 { rg int ls=ljl[now].ch[0];//左孩子 if(ljl[ls].size+ljl[now].cnt<x)//如果排名比左孩子總元素數和與我相等的數總和還大 { x-=ljl[ls].size+ljl[now].cnt;//就減去前面的元素數 now=ljl[now].ch[1];//去右孩子上找這個排名 } else if(ljl[ls].size>=x)now=ls;//如果左孩子裏包括它 else return ljl[now].v;//那就在這個點上了,返回 } } int main() { use=read(); Insert(Inf),Insert(-Inf); for(rg int i=1;i<=use;++i) { rg int opt=read(),x=read(); if(opt==1)Insert(x); if(opt==2)Delete(x); if(opt==3)find(x),printf("%d\n",ljl[ljl[root].ch[0]].size); if(opt==4)printf("%d\n",kth(x+1)); if(opt==5)printf("%d\n",ljl[Next(x,0)].v); if(opt==6)printf("%d\n",ljl[Next(x,1)].v); } return 0; }
luoguP3369[模板]普通平衡樹(Treap/SBT) 題解