关于树论【左偏树】
還記得當(dāng)年坐在OZY大佬旁邊被D的日子。。才發(fā)現(xiàn)現(xiàn)在妙已經(jīng)變成權(quán)限題做不了(怕是要被DS)只能補(bǔ)補(bǔ)左偏樹聊以自慰了。
這個(gè)東西呢其實(shí)也是堆的一種(也叫左偏堆),可以理解為維護(hù)大(小)根堆的,堆頂就是最大(小)值用d表示,然后l,r是左右孩子節(jié)點(diǎn),c是管理人數(shù)。至于為什么叫做左偏樹呢,是因?yàn)樗粋€(gè)奇怪的定義:左邊的子樹的節(jié)點(diǎn)數(shù)一定大于右邊的子樹的節(jié)點(diǎn)數(shù),這樣有什么好處呢?顯然,我們?nèi)绻迦牍?jié)點(diǎn)從左往右,那就可以保證樹的平衡。
其實(shí)這樣看來,這個(gè)東西挺普通的,只是方便求最大(小)值,或者再拓展一下,管理多個(gè)集合,但是,它有一個(gè)關(guān)鍵的操作,就是Merge——合并!他可以支持兩個(gè)左偏樹的合并,具體怎么做呢?跟插入一個(gè)點(diǎn)是一樣的,在右邊插入,然后通過交換左右孩子,繼續(xù)保持左偏性質(zhì),所以這個(gè)有一個(gè)限制——左右孩子可以交換,就是說不像伸展樹類似的,左大右小什么的。
bzoj1455(現(xiàn)在也變成權(quán)限題了)時(shí)光荏再,物是人非啊。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; struct heap {int l,r,c,d;heap(){l=0;r=0;c=0;} }h[1100000]; int Merge(int x,int y) {if(x==0||y==0)return x+y;if(h[x].d>h[y].d)swap(x,y);//維護(hù)小根堆 h[x].r=Merge(h[x].r,y);//讓他到右邊合并,令樹平衡 if(h[h[x].l].c<h[h[x].r].c)swap(h[x].l,h[x].r);//保持左偏 h[x].c=h[h[x].r].c+1;//維護(hù) return x; } int fa[1100000]; int findfa(int x)//用并查集來維護(hù)每一個(gè)團(tuán) {if(fa[x]==x)return x;fa[x]=findfa(fa[x]);return fa[x]; } bool v[1100000];//人有沒有被殺 char ss[5]; int main() {int n,m,x,y;scanf("%d",&n);for(int i=1;i<=n;i++){fa[i]=i;scanf("%d",&h[i].d);}memset(v,false,sizeof(v)); scanf("%d",&m);while(m--){scanf("%s",ss+1);if(ss[1]=='M'){scanf("%d%d",&x,&y);if(v[x]==true||v[y]==true)continue; int fx=findfa(x),fy=findfa(y);if(fx==fy)continue;//是否在一個(gè)團(tuán)里面 fa[fx]=fa[fy]=Merge(fx,fy);//合并兩個(gè)堆,維護(hù)并查集,祖先是值最小的人 }else{scanf("%d",&x);if(v[x]==true){printf("0\n");continue;}int k=findfa(x);v[k]=true;//殺人 printf("%d\n",h[k].d);fa[k]=Merge(h[k].l,h[k].r);//祖先是新的值最小的人 fa[fa[k]]=fa[k];}}return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/AKCqhzdy/p/7619895.html
總結(jié)
- 上一篇: 实习那些事儿
- 下一篇: [NIO系列]NIO源码分析之Buffe