loj3388. 「NOIP2020」移球遊戲
阿新 • • 發佈:2021-09-27
好題。這個思路是嫖來的(參見這裡)
對於每一種顏色分開處理。考慮將是當前顏色的球視為 \(1\),否則視為 \(0\),然後先構造一個全 \(0\) 列,具體就是先統計第一列 \(1\) 的個數 \(cnt\),然後第 \(n\) 列移動 \(cnt\) 個球到第 \(n+1\) 列,然後分解第一列, \(1\) 放在第 \(n\) 列,\(0\) 放在第 \(n+1\) 列。然後把 \(n+1\) 列的 \(m-cnt\) 個 \(0\) 扔回一號柱子,然後同樣的方法分解第 \(2\) 列,\(0\) 扔到第一列,\(1\) 扔到第 \(n+1\) 列(如果第一列滿了就全扔到最後一列)。由於 \(1\)
有了全 \(0\) 列考慮構造全 \(1\) 列。先把全 \(1\) 列換到第 \(n\) 列,最後一列空,我們用同樣的方法拆解第一列,這樣我們會發現第一列的 \(1\) 都到了第 \(n\) 列的頂端,第 \(n+1\) 列出現了一個新的全 \(0\) 列。於是可以用新的全 \(0\) 列重複這個過程,最後所有的 \(1\) 就都在列的頂端了,直接全部提出來到空柱子上面即可,最後用全 \(0\) 列去補上空位,然後遞迴處理即可。
但是我們發現當只剩兩種顏色的時候這個過程就無法進行了,因為柱子不夠。這個時候直接特判討論掉就好了。
最壞情況大約只需要 \(6\times10^5\) 次操作。
#include<iostream> #include<cstdio> #include<vector> using namespace std; int n,m,p[61]; vector<int> v[61]; vector<pair<int,int> > ans; inline int read() { int x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x; } void print(int x) { if(x>=10) print(x/10); putchar(x%10+'0'); } inline void move(int x,int y) { ans.push_back(make_pair(x,y)); v[y].push_back(v[x].back()); v[x].pop_back(); } inline int solve(int x,int col) { int cnt=0; for(auto i:v[x]) cnt+=i==col; return cnt; } inline void pt() { print((int)ans.size()); putchar('\n'); for(auto i:ans) { print(i.first); putchar(' '); print(i.second); putchar('\n'); } } inline void make0(int n,int col) { int cnt=solve(p[1],col); for(register int i=1;i<=cnt;++i) move(p[n],p[n+1]); while(!v[p[1]].empty()) if(v[p[1]].back()==col) move(p[1],p[n]); else move(p[1],p[n+1]); for(register int i=1;i<=m-cnt;++i) move(p[n+1],p[1]); while(!v[p[2]].empty()) if(v[p[2]].back()==col||(int)v[p[1]].size()==m) move(p[2],p[n+1]); else move(p[2],p[1]); p[1]^=p[n]^=p[1]^=p[n]; p[2]^=p[n+1]^=p[2]^=p[n+1]; } inline void make1(int n,int pos,int col) { int cnt=solve(p[pos],col); for(register int i=1;i<=cnt;++i) move(p[n],p[n+1]); while(!v[p[pos]].empty()) if(v[p[pos]].back()==col) move(p[pos],p[n]); else move(p[pos],p[n+1]); p[pos]^=p[n+1]^=p[pos]^=p[n+1]; p[pos]^=p[n]^=p[pos]^=p[n]; } inline void work(int n,int col) { for(register int i=1;i<n;++i) while(v[p[i]].back()==col) move(p[i],p[n+1]); for(register int i=1;i<n;++i) while((int)v[p[i]].size()<m) move(p[n],p[i]); } int main() { n=read(),m=read(); for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) v[i].push_back(read()); if(n==2) { int cnt=solve(1,1),tmp=0; for(register int i=1;i<=cnt;++i) move(2,3); while(!v[1].empty()) if(v[1].back()==1) { move(1,2); ++tmp; } else move(1,3); for(register int i=1;i<=tmp;++i) move(2,1); for(register int i=tmp+1;i<=m;++i) move(3,1); for(register int i=1;i<=cnt;++i) move(3,2); for(register int i=tmp+1;i<=m;++i) move(1,3); while(!v[2].empty()) if(v[2].back()==1) move(2,1); else move(2,3); pt(); return 0; } for(register int i=1;i<=n+1;++i) p[i]=i; for(register int i=n;i>=3;--i) { make0(i,i); for(register int j=1;j<i;++j) make1(i,j,i); work(i,i); } int cnt=solve(p[1],1),tmp=0; for(register int i=1;i<=cnt;++i) move(p[2],p[3]); while(!v[p[1]].empty()) if(v[p[1]].back()==1) { move(p[1],p[2]); ++tmp; } else move(p[1],p[3]); for(register int i=1;i<=tmp;++i) move(p[2],p[1]); for(register int i=tmp+1;i<=m;++i) move(p[3],p[1]); for(register int i=1;i<=cnt;++i) move(p[3],p[2]); for(register int i=tmp+1;i<=m;++i) move(p[1],p[3]); while(!v[p[2]].empty()) if(v[p[2]].back()==1) move(p[2],p[1]); else move(p[2],p[3]); pt(); return 0; }