NOIP2020移球遊戲
阿新 • • 發佈:2020-12-06
題意:
有\(n+1\)個柱子,每個柱子上最多放m個盤。
起初,第\(n+1\)個柱子是空的,其餘的柱子都放滿了盤。盤有n種顏色,每種各m個。
要求移動,使顏色歸位。
首先,考慮\(n=2\)的做法。即只有黑白兩種顏色。
可以這樣做:
先將所有黑色移到白色上面,然後就顯然了。
設1號柱子有x個黑色的。
那麼,將2號柱子最上面的x個盤移至3號,使2號柱子空出x個位置。
然後,對於1號柱子的所有盤,黑色的移到2號,白色的移到3號。
然後,把3號柱子上面的那些白色盤移到1號,再把2號柱子上面的那些黑色盤移到1號。
最後,把x個盤移回2號。
這樣,在保證2號柱子不動的前提下,整理了1號。
對於\(n>2\)
分治。
把一半顏色染成黑色,另一半染成白色。
按照上述做法整理所有柱子。
每次,挑選兩根柱子,利用空柱子,便可以整理出一根顏色只有黑或白的柱子。
這樣,把所有柱子都變成單色的(黑或白),就可以繼續分治了。
複雜度(操作次數):\(O(nmlogn)\)。
程式碼:
#include <stdio.h> int rs[402],M; struct Stk { int st[402],tp; Stk(){tp=0;} void push(int x) { st[tp++]=x; } int top() { return st[tp-1]; } int pop() { return st[--tp]; } int count() { int rt=0; for(int i=0;i<tp;i++) rt+=rs[st[i]]; return rt; } /*void print() { for(int i=0;i<tp;i++) printf("%d ",st[i]); }*/ }; Stk sz[52]; int ax[820010],ay[820010],sl=0,em,ss[52]; void move(int x,int y) { ax[sl]=x;ay[sl++]=y; sz[y].push(sz[x].pop()); } void up1(int b,int c) { int a=em,s=ss[b]; for(int i=0;i<s;i++) move(c,a); while(sz[b].tp) { if(rs[sz[b].top()]) move(b,c); else move(b,a); } for(int i=0;i<M-s;i++) move(a,b); for(int i=0;i<s;i++) move(c,b); for(int i=0;i<s;i++) move(a,c); } void rev(int &a) { ss[em]=M-ss[a]; for(int i=0;i<M;i++) move(a,em); int t=em; em=a;a=t; } void tog(int &a,int &b) { int fn[2]={0}; int sa=ss[a],sb=ss[b]; if(rs[sz[b].top()]!=rs[sz[a].top()]) fn[1]=1,sb=M-sb; if(sa+sb>M) fn[0]^=1,fn[1]^=1; if(fn[0])rev(a); if(fn[1])rev(b); if(ss[a]>ss[b]) { int t=a; a=b;b=t; } for(int i=0;i<ss[a];i++) move(a,em); for(int i=0;i<ss[b];i++) move(b,em); for(int i=0;i<ss[a];i++) move(b,a); for(int i=0;i<ss[a]+ss[b];i++) move(em,b); ss[b]+=ss[a]; } void dfs(int zz[52],int co[52],int n) { if(n<=1) return; int m=n/2; for(int i=0;i<m;i++)rs[co[i]]=1; for(int i=m;i<n;i++)rs[co[i]]=0; for(int i=0;i<n;i++)ss[zz[i]]=sz[zz[i]].count(); for(int i=0;i<n;i++)up1(zz[i],zz[(i+1)%n]); int zl[52],a=0,zr[52],b=0; for(int i=0;i<n-1;i++) tog(zz[i],zz[i+1]); for(int i=0;i<n;i++) { int t=zz[i]; if(rs[sz[t].top()]) zl[a++]=t; else zr[b++]=t; } dfs(zl,co,m); dfs(zr,co+m,n-m); } int main() { //freopen("ball.in","r",stdin); //freopen("ball.out","w",stdout); int n; scanf("%d%d",&n,&M); for(int i=1;i<=n;i++) { for(int j=0;j<M;j++) { int a; scanf("%d",&a); sz[i].push(a); } } em=n+1; int zz[52],co[52]; for(int i=0;i<n;i++) zz[i]=co[i]=i+1; dfs(zz,co,n); printf("%d\n",sl); for(int i=0;i<sl;i++) printf("%d %d\n",ax[i],ay[i]); return 0; }