loj3388. 「NOIP2020」移球游戏
好题。这个思路是嫖来的(参见这里)
对于每一种颜色分开处理。考虑将是当前颜色的球视为 \(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\) 最多 \(m\) 个,前两个柱子上至少有 \(m\) 个 \(0\) 因此肯定可以填满第一列。
有了全 \(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;
}