本文参考了洛谷Dzhao大佬的题解
首先,做这种构造题,一定要想出一种普适性的方法,不能去想所谓的“最优解”,而是要去想一种在不同的情况下都能用的方法。
其次,部分分是一种提示满分的思路。
如果只有2个柱子,怎么做呢?(设颜色为0,1)
柱子1 m个球
柱子2 m个球
柱子3 0个球
Step 1
首先设柱子1的m个球中有sum个1
将柱子2的顶上sum个球移到柱子3
那么情况变成
柱子1 m个球
柱子2 m-sum个球
柱子3 sum个球
Step 2
将柱子1中球一个个提出来,是颜色1放柱子2上,颜色0放柱子3上
情况变成
柱子1 0个球
柱子2 m-sum+sum=m个球
柱子3 sum+m-sum=m个球
Step 3
现在柱子2顶上有sum个颜色为1的球,将它们移到柱子1
对于柱子3顶上m-sum个颜色为0的球同理
然后将柱子2的球全部移到柱子3
情况变成
柱子1 m个球(sum个连续1,m-sum个连续0)
柱子2 0个球
柱子3 sum+m-sum=m个球
Step 4
将柱子1顶上m-sum个颜色为0的球移到柱子2
情况变成
柱子1 sum个连续颜色为1的球
柱子2 m-sum个连续颜色为0的球
柱子3 m个球
我们现在完成了将颜色0,1球分离的伟大壮举
Step 5
移动柱子3的球,颜色为1的移到柱子1,颜色为0的移到柱子2
步骤完成,现在柱子1上就是m个颜色为1的球,柱子2上就是m个颜色为0的球,柱子3仍然是空的。
在n=2时,我们得到了一个普适性的方法。
如何扩展n的范围?
我们考虑分治。
钦定一个数,使区间内小于等于它的数设为1,大于设为0
这个数取区间中点最优
那么每次对于区间,枚举处理两根交换的柱子跨过区间中点的情况,然后分治递归继续向下处理即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 55
#define M 405
#define Lim 820005
using namespace std;
int len[N],a[N][M],bz[N];
int i,j,k,m,n,o,p,l,s,t,flag;
int ans[Lim][2],tot;
void read(int &x)
{
char ch=getchar();x=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
}
void move(int i,int j) {ans[++tot][0]=i,ans[tot][1]=j,len[j]++,a[j][len[j]]=a[i][len[i]],len[i]--;}
void dg(int l,int r)
{
if (l==r) return;
int mid=l+r>>1;
memset(bz,0,sizeof(bz));
for (int pos1=l;pos1<=mid;pos1++)
for (int pos2=mid+1;pos2<=r;pos2++)
{
int i=pos1,j=pos2;
if (bz[i]||bz[j]) continue;
s=flag=0;
for (int k=1;k<=m;k++) s+=(a[i][k]<=mid)+(a[j][k]<=mid);
if (s<m) swap(i,j),flag=1;
s=0;
for (int k=1;k<=m;k++) s+=(!flag?a[i][k]<=mid:a[i][k]>mid);
for (int k=1;k<=s;k++) move(j,n+1);
for (k=len[i];k>=1;k--)
{
k=len[i];
if ((!flag?a[i][k]<=mid:a[i][k]>mid)) move(i,j);
else move(i,n+1);
}
for (int k=1;k<=s;k++) move(j,i);
for (int k=1;k<=m-s;k++) move(n+1,i);
for (int k=1;k<=m-s;k++) move(j,n+1);
for (int k=1;k<=m-s;k++) move(i,j);
for (k=len[n+1];k>=1;k--)
{
k=len[n+1];
if (len[i]!=m&&((!flag?a[n+1][k]<=mid:a[n+1][k]>mid)||len[j]==m)) move(n+1,i);
else move(n+1,j);
}
bz[i]=1;
}
dg(l,mid),dg(mid+1,r);
}
int main()
{
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
read(n),read(m);
for (i=1;i<=n;len[i]=m,i++)
for (j=1;j<=m;j++)
read(a[i][j]);
dg(1,n);
printf("%d\n",tot);
for (i=1;i<=tot;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
return 0;
}