Description
小 C 正在玩一个移球游戏,他面前有 $n + 1$ 根柱子,柱子从 $1 \sim n + 1$ 编号,其中 $1$ 号柱子、$2$ 号柱子、……、$n$ 号柱子上各有 $m$ 个球,它们自底向上放置在柱子上,$n + 1$ 号柱子上初始时没有球。这 $n \times m$ 个球共有 $n$ 种颜色,每种颜色的球各 $m$ 个。
初始时一根柱子上的球可能是五颜六色的,而小 C 的任务是将所有同种颜色的球移到同一根柱子上,这是唯一的目标,而每种颜色的球最后放置在哪根柱子则没有限制。
小 C 可以通过若干次操作完成这个目标,一次操作能将一个球从一根柱子移到另一根柱子上。更具体地,将 $x$ 号柱子上的球移动到 $y$ 号柱子上的要求为:
1. $x$ 号柱子上至少有一个球;
2. $y$ 号柱子上至多有 $m - 1$ 个球;
3. 只能将 $x$ 号柱子最上方的球移到 $y$ 号柱子的最上方。
小 C 的目标并不难完成,因此他决定给自己加加难度:在完成目标的基础上,使用的操作次数不能超过 $820000$。换句话说,小 C 需要使用至多 $820000$ 次操作完成目标。
小 C 被难住了,但他相信难不倒你,请你给出一个操作方案完成小 C 的目标。合法的方案可能有多种,你只需要给出任意一种,题目保证一定存在一个合法方案。
Solution
当仅有两个柱子时,有这样的方案:
钦定A柱上放置1号球,B柱上放置2号球,设A柱上原有$s$个1号球
1.从B柱向C柱移动$s$个球
2.将A柱上1号球置于B柱,2号球置于C柱
3.从B柱向A柱移动$s$个球,从C柱向A柱移动$m-s$个球(以上的操作是为了给A柱排序)
4.从B柱向C柱移动$m-s$个球(此时B柱空)
5.从A柱向B柱移动$m-s$个球(此时A,B柱都符合要求但不满)
6.从C柱按编号向A,B柱移动
总操作次数为$5m-s$
有多个柱时考虑分治,设阈值为$\frac{l+r}{2}$,小于阈值的定为1号球,其余的为2号球,$O(n^2)$地枚举两根柱子进行操作使至少一根柱子满足要求
总操作次数$5mn\log n$
时间复杂度$O(5mn^2\log n)$
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m,sta[55][405],top[55],sum,ans[820005][2],tot; bool tag[55]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } void tran(int x,int y) { ans[++tot][0]=x,ans[tot][1]=y,sta[y][++top[y]]=sta[x][top[x]--]; } void solve(int l,int r) { if(l==r) return; int mid=l+r>>1; memset(tag,false,sizeof(tag)); for(int i=l;i<=mid;i++) for(int j=mid+1;j<=r;j++) { if(tag[i]||tag[j]) continue; sum=0; for(int k=1;k<=m;k++) sum+=(sta[i][k]<=mid)+(sta[j][k]<=mid); if(sum>=m) { sum=0; for(int k=1;k<=m;k++) sum+=(sta[i][k]<=mid); for(int k=1;k<=sum;k++) tran(j,n+1); for(int k=1;k<=m;k++) if(sta[i][top[i]]<=mid) tran(i,j); else tran(i,n+1); for(int k=1;k<=sum;k++) tran(j,i); for(int k=1;k<=m-sum;k++) tran(n+1,i); for(int k=1;k<=m-sum;k++) tran(j,n+1); for(int k=1;k<=m-sum;k++) tran(i,j); for(int k=1;k<=m;k++) if(sta[n+1][top[n+1]]<=mid&&top[i]<m) tran(n+1,i); else tran(n+1,j); tag[i]=true; } else { sum=0; for(int k=1;k<=m;k++) sum+=(sta[j][k]>mid); for(int k=1;k<=sum;k++) tran(i,n+1); for(int k=1;k<=m;k++) if(sta[j][top[j]]>mid) tran(j,i); else tran(j,n+1); for(int k=1;k<=sum;k++) tran(i,j); for(int k=1;k<=sum;k++) tran(n+1,j); for(int k=1;k<=m-sum;k++) tran(i,n+1); for(int k=1;k<=m-sum;k++) tran(j,i); for(int k=1;k<=m;k++) if(sta[n+1][top[n+1]]>mid&&top[j]<m) tran(n+1,j); else tran(n+1,i); tag[j]=true; } } solve(l,mid),solve(mid+1,r); } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sta[i][++top[i]]=read(); solve(1,n); printf("%d\n",tot); for(int i=1;i<=tot;i++) printf("%d %d\n",ans[i][0],ans[i][1]); return 0; }移球游戏