Luogu P3369 【模板】普通平衡树
生活随笔
收集整理的這篇文章主要介紹了
Luogu P3369 【模板】普通平衡树
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Luogu P3369 【模板】普通平衡樹
啊 splay啊
代碼:
#include<cstdio> #include<cstring> #include<algorithm>//左兒子一定比我小 右兒子一定比我大 /* * <----0| * <----rt / \* */ \ / \* * * * 這是一棵樹 */ struct nod1{int ch[2],c,fa,size,cnt;}tr[110010]; //ch[0]是左兒子編號 ch[1]是右兒子比那好 //c是節點數值 fa是節點爸爸 size是以節點為根子樹大小 //cnt是和節點數值一樣的數的個數 int n,rt,tot; //n是操作數 rt是根 tot是整個數的節點個數,添加新節點時用到 void update(int x)//更新維護 此處x是編號 {tr[x].size=tr[tr[x].ch[0]].size+tr[x].cnt+tr[tr[x].ch[1]].size;//x子樹大小=x左子樹+x右子樹+x節點相同數個數 return ; }//okint findip(int x)//找x數值的節點編號 此處x是數值 {int now=rt;//從根開始找 while(x!=tr[now].c){if(x<tr[now].c)//比當前點數值小 {if(tr[now].ch[0]==0)break;//沒有,只好跳出循環 返回當前這個now 是與x最貼切的節點 now=tr[now].ch[0];//走左邊 }else{if(tr[now].ch[1]==0)break;now=tr[now].ch[1];//走右邊 }}return now; }void add(int c,int x)//添加新節點 此處c是數值,x是新節點爸爸的編號 {tot++;tr[tot].c=c;tr[tot].cnt=1;tr[tot].size=1;tr[tot].fa=x;tr[tot].ch[0]=tr[tot].ch[1]=0;if(c<tr[x].c)tr[x].ch[0]=tot;else tr[x].ch[1]=tot;//判斷新節點是爸爸的左兒子還是右兒子 return ; }void rotate(int x,int w)//單旋 此處x是編號 w是兒子標記 w等于1意味著我是我爸的左兒子,否則為我爸的右兒子 {int f=tr[x].fa,ff=tr[f].fa;tr[f].ch[1-w]=tr[x].ch[w];//這里的ch[0]是左兒子,ch[1]是右兒子//如果我是我爸的右兒子,那么說明我的整棵子樹都比他大,所以我的左兒子給我爸當右兒子//如果我是我爸的左兒子,那么說明我的整棵子樹都比他小,所以我的右兒子給我爸當左兒子if(tr[x].ch[w]!=0)tr[tr[x].ch[w]].fa=f; if(tr[ff].ch[0]==f)tr[ff].ch[0]=x;//爺爺>爸爸 else tr[ff].ch[1]=x;//爸爸是爺爺什么兒子 我旋轉后就給爺爺當什么兒子 tr[x].fa=ff;tr[f].fa=x;tr[x].ch[w]=f;update(f);//更新底層 update(x);//更新當前層 }void splay(int x,int goal)//把x旋到目標節點下面 {while(tr[x].fa!=goal){int f=tr[x].fa,ff=tr[f].fa;if(ff==goal)//爺爺就是目標{//旋一次就行 if(x==tr[f].ch[0])//我<我爸 rotate(x,1);else rotate(x,0);} else{if(tr[ff].ch[0]==f)//爺爺>我爸 {if(tr[f].ch[0]==x)//我爸>我 {//我與我爸共線 則雙旋rotate(f,1);//我爸是我爺爺左兒子 rotate(x,1);//我是我爸左兒子 }else{//我爸<我 rotate(x,0);//我是我爸右兒子 rotate(x,1);//現在我是我爺爺左兒子}} else//爺爺<我爸 { if(tr[f].ch[0]==x){rotate(x,1);//我是我爸左兒子 rotate(x,0);//現在我是我爺爺右兒子 }else{//我與我爸共線 則雙旋rotate(f,0);//我爸是我爺爺右兒子 rotate(x,0);//我是我爸右兒子 }}} }if(goal==0)rt=x; }void insert(int x)//插入一個數 此處x是數值 {if(rt==0){//第一個節點 先讓你當0兒子 add(x,0);rt=tot;return ;}int ip=findip(x);//找出x數值的節點編號或是最貼近的節點 if(tr[ip].c==x){//之前插入過這個數值 tr[ip].cnt++;update(ip);splay(ip,0);}else{add(x,ip);//新點 update(ip);splay(tot,0);} }void del(int x)//刪除一個數 此處x是數值 {int ip=findip(x);//拿它編號 splay(ip,0);//讓它當根 方便操作 if(tr[ip].c!=x)return ;//之前沒有插入過這個數值 if(tr[ip].cnt>1){//不止一個這個數值 tr[ip].cnt--;update(ip);}else if(tr[ip].ch[0]==0&&tr[ip].ch[1]==0)//已經將這個點旋到根了 如果沒有左右兒子 說明整棵樹只有一個點 rt=0,tot=0;else if(tr[ip].ch[0]!=0&&tr[ip].ch[1]==0)//只有一個兒子就讓他繼承 rt=tr[ip].ch[0],tr[tr[ip].ch[0]].fa=0;else if(tr[ip].ch[1]!=0&&tr[ip].ch[0]==0)//只有一個兒子就讓他繼承 rt=tr[ip].ch[1],tr[tr[ip].ch[1]].fa=0;else{int p=tr[ip].ch[0];while(tr[p].ch[1]!=0)p=tr[p].ch[1];//找前驅來 splay(p,ip);//旋到我這里來,因為前驅是小于我中最大的 它此時沒有右兒子 rt=p;tr[p].fa=0;tr[tr[ip].ch[1]].fa=p;//直接把右兒子給它 tr[p].ch[1]=tr[ip].ch[1];update(p);} }int rank(int x)//此處x是數值 找出x的排名 {int ip=findip(x);splay(ip,0);//把我旋成根 方便處理 return tr[tr[ip].ch[0]].size+1;//我是根 所以左子樹全小于我 +1就是我排名 }int kth(int x)//找排名是x的數值 {int now=rt;while(1){if(x<=tr[tr[now].ch[0]].size)//如果我小于左邊的個數 去左邊找 now=tr[now].ch[0];else if(tr[tr[now].ch[0]].size+tr[now].cnt<x){//如果比左+當前自己還大 去右邊 x=x-tr[tr[now].ch[0]].size-tr[now].cnt;now=tr[now].ch[1];} else break;//等于當前 }return tr[now].c; }int last(int x)//找前驅 此處x是數值 {int ip=findip(x);splay(ip,0);if(x<=tr[ip].c&&tr[ip].ch[0]!=0){ip=tr[ip].ch[0];while(tr[ip].ch[1]!=0)ip=tr[ip].ch[1];} //比我小最大的:左子樹最右的 if(x<=tr[ip].c)return 0;return tr[ip].c; }int next(int x)//找后繼 此處x是數值 {int ip=findip(x);splay(ip,0);if(x>=tr[ip].c&&tr[ip].ch[1]!=0){ip=tr[ip].ch[1];while(tr[ip].ch[0]!=0)ip=tr[ip].ch[0]; } //比我大最小的:右子樹最左邊if(x>=tr[ip].c)return 0;return tr[ip].c; }int main() {scanf("%d",&n);for(int i=1;i<=n;i++){int h,x;scanf("%d %d",&h,&x);if(h==1)insert(x);else if(h==2)del(x);else if(h==3)printf("%d\n",rank(x));else if(h==4)printf("%d\n",kth(x));else if(h==5)printf("%d\n",last(x));else if(h==6)printf("%d\n",next(x));} }看了好多博客 要昏了
總結
以上是生活随笔為你收集整理的Luogu P3369 【模板】普通平衡树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: span标签的间距问题
- 下一篇: 刀片服务器的显示切换,刀片机服务器切换