嘘~ 正在从服务器偷取页面 . . .


洛谷P7115 [NOIP2020] 移球游戏 题解

题目链接:P7115 [NOIP2020] 移球游戏

题意

小 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 的目标。合法的方案可能有多种,你只需要给出任意一种,题目保证一定存在一个合法方案。

输入格式

第一行两个用空格分隔的整数 $n, m$。分别表示球的颜色数、每种颜色球的个数。

接下来 $n$ 行每行 $m$ 个用单个空格分隔的整数,第 $i$ 行的整数按自底向上的顺序依次给出了 $i$ 号柱子上的球的颜色。

输出格式

本题采用自定义校验器(special judge)评测。

你的输出的第一行应该仅包含单个整数 $k$,表示你的方案的操作次数。你应保证 $0 \le k \le 820000$。

接下来 $k$ 行每行你应输出两个用单个空格分隔的正整数 $x, y$,表示这次操作将 $x$ 号柱子最上方的球移动到 $y$ 号柱子最上方。你应保证 $1 \le x, y \le n + 1$ 且 $x \ne y$。

数据范围

保证 $2 \le n \le 50$,$2 \le m \le 400$。

考虑 $n=2$ 的情况怎么做。设第一根柱子上有 $c$ 个 $1$ ,以及 $m-c$ 个 $2$ 。

考虑以下方案:(如果看不懂可以看一下参考文献1的图)

  1. 将第二根柱子(没错,就是第二根柱子)最上面的 $c$ 个球放到第三个柱子上

  2. 将第一根柱子上的所有 $1$ 放到第二根柱子上,其余的放到第三根柱子上

    此时第二根柱子有 $(m-c)+c$ 个球,第三根柱子有 $c+(m-c)$ 个球

  3. 然后把第二根柱子上的 $c$ 个 $1$ 放到第一根柱子上,再把第三根柱子的 $m-c$ 个 $2$ 放到第一根柱子上

  4. 接着把第二根柱子剩余的 $m-c$ 个球放到第三根柱子上

  5. 再把第一根柱子的 $m-c$ 个 $2$ 放到第二根柱子上

  6. 此时我们只需要将第三根柱子上的球按 $1,2$ 分配到第一、二根柱子上,就完成了。

  7. 操作数 $c + m + m + (m-c) + (m - c) + m = 5m - c$ 。

接着考虑 $n > 2$ 的情况。

如果我们钦定一个 $x$ ,把小于等于 $x$ 的数看作 $1$ ,大于 $x$ 的数看作 $2$ 。

那么一次操作后,相当于把所有小于等于 $x$ 的都放到了一侧,其余的在另一侧。

考虑分治的解决问题,此时 $x$ 取区间中点 $\left\lfloor\frac{l + r}{2}\right\rfloor$ 显然是最优的。

我们将位于左右两侧的 $i,j$ 一一配对,由于操作数和 $c$ 有关,所以我们选择选择 $c$ 较大的那个操作。

这样总操作次数为

时间复杂度为

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll; 
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(55))
#define M ((int)(444))
#define L ((int)(1e6 + 15))

bool vis[N];
int n, m, res, top[N], a[N][M], ans[L][2];
void move(int x, int y)
{
    ++res; ans[res][0] = x, ans[res][1] = y;
    a[y][++top[y]] = a[x][top[x]--];
}
void solve(int l, int r)
{
    if(l == r) return;
    int mid = (l + r) >> 1; memset(vis, 0, sizeof(vis));
    rep(i, l, mid) rep(j, mid + 1, r)
    {
        if(vis[i] || vis[j]) continue;
        int cnt = 0;
        rep(k, 1, m) cnt += (a[i][k] <= mid);
        rep(k, 1, m) cnt += (a[j][k] <= mid);
        if(cnt >= m)
        {
            cnt = 0; rep(k, 1, m) cnt += (a[i][k] <= mid);
            rep(k, 1, cnt) move(j, n + 1);
            while(top[i]) { 
                if(a[i][top[i]] <= mid) move(i, j);
                else move(i, n + 1);
            }
            rep(k, 1, cnt) move(j, i);
            rep(k, 1, m - cnt) move(n + 1, i);
            rep(k, 1, m - cnt) move(j, n + 1);
            rep(k, 1, m - cnt) move(i, j);
            while(top[n + 1]) { 
                if(top[i] == m || a[n + 1][top[n + 1]] > mid) move(n + 1, j); 
                else move(n + 1, i);
            }
            vis[i] = true;
        }else
        {
            cnt = 0; rep(k, 1, m) cnt += (a[j][k] > mid);
            rep(k, 1, cnt) move(i, n + 1);
            while(top[j]) { 
                if(a[j][top[j]] > mid) move(j, i);
                else move(j, n + 1);
            }
            rep(k, 1, cnt) move(i, j);
            rep(k, 1, m - cnt) move(n + 1, j);
            rep(k, 1, m - cnt) move(i, n + 1);
            rep(k, 1, m - cnt) move(j, i);
            while(top[n + 1]) {
                if(top[j] == m || a[n + 1][top[n + 1]] <= mid) move(n + 1, i);
                else move(n + 1, j);
            }
            vis[j] = true;
        }
    }
    solve(l, mid); solve(mid + 1, r);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; int x; 
    rep(i, 1, n) rep(j, 1, m) { cin >> x; a[i][++top[i]] = x; }
    solve(1, n); cout << res << '\n';
    rep(i, 1, res) cout << ans[i][0] << ' ' << ans[i][1] << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/pral7fv1


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录