【BZOJ2733】[HNOI2012] 永無鄉(啟發式合併Splay)
阿新 • • 發佈:2018-11-01
大致題意: 給你一張圖,其中每個點有一個權值,有兩種操作:在兩點之間連一條邊,詢問一個點所在聯通塊第\(k\)小的權值。
平衡樹
看到第\(k\)小,應該不難想到平衡樹。
為了練習\(Splay\),所以我是用\(Splay\)來做這題的。
對於詢問操作
對於詢問操作,我們只要找到該節點所在\(Splay\)的根,然後查詢第\(k\)小的權值即可,應該是\(Splay\)比較模板的操作吧。
因此就不多說了。
下面讓我們來重點看一看連邊操作。
對於連邊操作
這才是真正噁心的操作。
考慮這條邊連線的兩個節點如果是在同一聯通塊,則完全不必考慮這條邊。
但如果連線的是兩個聯通塊,我們就需要合併這兩個\(Splay\)
至於如何合併,自然是啟發式合併了。
而啟發式合併的操作其實也非常簡單,就是遍歷較小的\(Splay\),然後把它裡面的節點一個一個插入至較大的\(Splay\)中。
這樣的時間複雜度看似極高,實際上均攤之後依然是可以接受的。
具體實現可以見程式碼。
程式碼
#include<bits/stdc++.h> #define N 100000 #define M 300000 #define ull unsigned long long #define swap(x,y) (x^=y^=x^=y) using namespace std; int n,m,a[N+5]; class FIO { private: #define Fsize 100000 #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++) #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch)) int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize]; public: inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));} inline void read_alpha(char &x) {while(!isalpha(x=tc()));} inline void writeln(int x) {if(!x) return pc('0'),pc('\n');if(x<0) pc('-'),x=-x;while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);pc('\n');} inline void clear() {fwrite(Fout,1,FoutSize,stdout);} }F; class Class_splay//Splay模板 { private: #define PushUp(x) (node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+1) #define Which(x) (node[node[x].Father].Son[1]==x) #define Connect(x,y,d) (node[node[x].Father=y].Son[d]=x) int rt,tot,data[N+5]; struct Tree { int Size,Father,Son[2]; inline void Clear() {Size=1,Father=Son[0]=Son[1]=0;} }node[(N+M<<1)+5]; inline void Rotate(int x,int &k) { register int fa=node[x].Father,pa=node[fa].Father,d=Which(x); (fa^k?node[pa].Son[Which(fa)]=x:k=x),node[x].Father=pa,Connect(node[x].Son[d^1],fa,d),Connect(fa,x,d^1),PushUp(fa),PushUp(x); } inline void Splay(int x,int &k) {for(register int fa=node[x].Father;x^k;Rotate(x,k),fa=node[x].Father) if(fa^k) Rotate(Which(x)^Which(fa)?x:fa,k);} inline void Insert(int &x,int pos,int lst) { if(!x) return (void)(node[x=pos].Clear(),node[x].Father=lst); Insert(node[x].Son[a[x]<a[pos]],pos,x),PushUp(x); } inline void dfs(int x,int rt)//遍歷較小的Splay,將其節點一個個插入較大的Splay中 { if(node[x].Son[0]) dfs(node[x].Son[0],rt); if(node[x].Son[1]) dfs(node[x].Son[1],rt); Insert(rt,x,0),Splay(x,rt);//插入 } public: inline void Init() {for(register int i=1;i<=n;++i) node[i].Size=1;} inline void Union(int x,int y)//啟發式合併x和y { while(node[x].Father) x=node[x].Father;//找到根節點 while(node[y].Father) y=node[y].Father;//找到根節點 if(!(x^y)) return;//如果在同一個聯通塊內就退出函式 if(node[x].Size<node[y].Size) swap(x,y); dfs(y,x); } inline int get_val(int x,int rk) { while(node[x].Father) x=node[x].Father; if(node[x].Size<rk) return -1; while(x) { if(node[node[x].Son[0]].Size>=rk) x=node[x].Son[0]; else if(node[node[x].Son[0]].Size+1==rk) return x; else rk-=node[node[x].Son[0]].Size+1,x=node[x].Son[1]; } } }splay; int main() { register int i,Q,x,y;register char op; for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]); for(splay.Init(),i=1;i<=m;++i) F.read(x),F.read(y),splay.Union(x,y); for(F.read(Q);Q;--Q) { if(F.read_alpha(op),F.read(x),F.read(y),op^'Q') splay.Union(x,y); else F.writeln(splay.get_val(x,y)); } return F.clear(),0; }