【BZOJ3506】[CQOI2014] 排序機械臂(Splay)
阿新 • • 發佈:2019-01-07
大致題意: 給你\(n\)個數。第一次找到最小值所在位置\(P_1\),翻轉\([1,P_1]\),第二次找到剩餘數中最小值所在位置\(P_2\),翻轉\([2,P_2]\),以此類推。求\(P_1,P_2,...,P_n\)的值。
關於洛谷上的四倍經驗
這題在洛谷上是一道四倍經驗題(目前看來是兩黑兩紫):
- 【洛谷3165】[CQOI2014] 排序機械臂
- 【洛谷4402】[CERC2007] robotic sort 機械排序
- 【SP2059】CERC07S - Robotic Sort
- 【UVA1402】Robotic Sort
初始化
這題應該是一道比較裸的 \(Splay\)題。
首先,我們將原陣列排序一遍,記下每一次操作的位置。
有一個細節,題目要求相同值要先取位置靠前的(沒注意到這點結果狂\(WA\)不止)。
然後便是建樹。
注意,\(Splay\)建樹的過程中我們一般會在序列左右各加一個多餘節點,方便後面取出一段區間進行操作。
操作
每一次操作,我們把要操作的位置先\(Splay\)到根,然後就可以得出答案即為此時左子樹的\(Size\),記其為\(ans\)。
注意是\(Size\)而不是\(Size+1\),要考慮到我們在序列左邊加的那個多餘節點已經使\(Size\)比實際存在的節點個數多\(1\)了。
然後,按照題目要求,我們要翻轉區間\([i,ans]\)
直接將\(i\)號節點和\(ans+2\)號節點分別旋到根節點和根節點的右兒子,然後翻轉根節點的右兒子的左兒子即可。
程式碼
#include<bits/stdc++.h> #define Type template<typename I> #define N 100000 #define swap(x,y) (x^=y^=x^=y) #define INF 1e9 using namespace std; int n; struct Data { int pos,val; inline friend bool operator < (Data x,Data y) {return x.val^y.val?x.val<y.val:x.pos<y.pos;} }a[N+5]; class 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) (FoutSize^Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch)) #define Isi(x) (typeid(x).name()==typeid(1).name()) #define Isc(x) (typeid(x).name()==typeid('a').name()) int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize]; public: Class_FIO() {A=B=Fin;} Type inline void read(I& x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));} Type inline void write(I x) { if(Isi(x)) {while(Stack[++Top]=x%10+48,x/=10);while(Top) pc(Stack[Top--]);} if(Isc(x)) pc(x); } template<typename I,typename... A> inline void read(I& x,A&... y) {read(x),read(y...);} template<typename I,typename... A> inline void write(I x,A... y) {write(x),write(y...);} inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;} }F; class Class_Splay//Splay { private: #define SIZE N #define PushUp(x) (node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+1) #define Rever(x) (swap(node[x].Son[0],node[x].Son[1]),node[x].Rev^=1) #define PushDown(x) (node[x].Rev&&(Rever(node[x].Son[0]),Rever(node[x].Son[1]),node[x].Rev=0)) #define Which(x) (node[node[x].Father].Son[1]==x) #define Connect(x,y,d) (node[node[x].Father=y].Son[d]=x) #define Split(x,y) (Splay(get_pos(x),rt),Splay(get_pos((y)+2),node[rt].Son[1]),node[node[rt].Son[1]].Son[0]) int rt; struct Tree { int Size,Rev,Father,Son[2]; }node[SIZE+5]; inline void Rotate(int x,int& k) { register int fa=node[x].Father,pa=node[fa].Father,d=Which(x);PushDown(fa),PushDown(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) {register int fa;while(x^k) fa=node[x].Father,fa^k&&(Rotate(Which(x)^Which(fa)?x:fa,k),0),Rotate(x,k);} inline void Build(int l,int r,int& rt) { register int mid=l+r>>1; if(node[rt=mid].Size=1,!(l^r)) return; l<mid&&(Build(l,mid-1,node[rt].Son[0]),node[node[rt].Son[0]].Father=rt), r>mid&&(Build(mid+1,r,node[rt].Son[1]),node[node[rt].Son[1]].Father=rt), PushUp(rt); } inline int get_pos(int rk) { register int x=rt; while(x) { if(PushDown(x),node[node[x].Son[0]].Size>=rk) x=node[x].Son[0]; else if(!(rk-=node[node[x].Son[0]].Size+1)) return x; else x=node[x].Son[1]; } } public: inline void Init(int len) {Build(1,len+2,rt);} inline int GetAns(int x) { register int k,ans; Splay(a[x].pos+1,rt),ans=node[node[rt].Son[0]].Size,k=Split(x,ans),Rever(k);//找到ans,然後翻轉 return ans;//返回答案 } #undef SIZE }Splay; int main() { register int i,p; for(F.read(n),i=1;i<=n;++i) F.read(a[a[i].pos=i].val);//讀入 for(sort(a+1,a+n+1),Splay.Init(n),i=1;i<=n;++i) F.write(p=Splay.GetAns(i),' ');//初始化排序+依次操作 return F.clear(),0; }