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;
}
posted @ 2021-09-27 20:18  绝顶我为峰  阅读(97)  评论(0编辑  收藏  举报