Loading

题解:[ICPC 2021 Seoul R] Squid Game

不妨设 \(X\le Y\le Z\),目标状态是 \(X=0\)。操作次数上限不大,尝试考虑每次让 \(X\) 成比例减少(例如折半)。

\(a=\frac{Y}{X},b=\frac{Z}{X}\)。当 \(a\) 为正整数时考虑以下流程:

  1. \(a\) 为偶数,将 \(Z\)\(X\) 倒,那么 \(Z\leftarrow Z-X,X\leftarrow 2X\),故 \(a\leftarrow\frac{a}{2},b\leftarrow\frac{b-1}{2}\)
  2. \(a\) 为奇数,将 \(Y\)\(X\) 倒,那么 \(Y\leftarrow Y-X,X\leftarrow 2X\),故 \(a\leftarrow\frac{a-1}{2},b\leftarrow\frac{b}{2}\)

重复直到 \(a=0\),此时有 \(Y=0\)

一个小问题是在这个过程中第一种操作会使得 \(Z<Y,b<a\)。不过容易归纳说明一定有 \(b\ge a-1\),所以在 \(a\) 为偶数时有 \(a\ge 2,b\ge 1\),一定可以进行操作。

考虑 \(a\) 不为偶数时,将上面过程中的 \(a\) 换成 \(\lfloor a\rfloor\),最后会得到 \(Y^\prime=Y_0\bmod X_0\)(注意此处右边的 \(X_0,Y_0\) 是这个流程开始前的 \(X,Y\))。此时最小值一定不超过 \(Y_0\bmod X_0\)

设新的最小值为 \(X^{\prime}\)

\(X^\prime\le\frac{X_0}{2}\) 时,就直接达到了最小值减半的目标。

\(X^\prime>\frac{X_0}{2}\) 时,有 \(X_0-X^\prime\le\frac{X_0}{2}\)。考虑调整一下上面的过程,具体地,将一开始的 \(Y_0\) 替换为 \(Y_0+X_0\),那么在进行完上面的过程后(假如倒水没有当前水量限制,即可以出现负数的水量),实际的 \(Y^\prime= (Y_0+X_0)\bmod X_0-X_0=X^\prime-X_0\)。将最后一步出现负数的操作的两个桶反过来倒,就会得到 \(X_0-X^\prime\)

设值域为 \(V\)。一次折半流程操作次数为 \(\log V\),共进行 \(\log V\) 次,总次数 \(\log^2 V\)(可能再加上一些低阶项)。

代码比较简单,可以帮助理解:

#include <bits/stdc++.h>
typedef long long LL;
typedef std::pair<int, int> pii;
#define MP std::make_pair
#define fi first
#define se second
std::vector<pii> ans;
void add(pii &a, pii &b)
{ a.fi -= b.fi, b.fi <<= 1, ans.emplace_back(a.se, b.se); }
void calc(pii a, pii b, pii c)
{
    if (a > b) std::swap(a, b);
    if (a > c) std::swap(a, c);
    if (b > c) std::swap(b, c);
    if (!a.fi) return ;
    int q = b.fi / a.fi + (b.fi % a.fi > a.fi / 2);
    for (; q; q >>= 1)
        if (q & 1) a.fi <= b.fi ? add(b, a) : add(a, b);
        else add(c, a);
    calc(a, b, c);
}
int main()
{
    int a, b, c; scanf("%d %d %d", &a, &b, &c);
    calc(MP(a, 1), MP(b, 2), MP(c, 3));
    printf("%d\n", ans.size());
    for (pii p : ans) printf("%d %d\n", p.fi, p.se);
    return 0;
}
posted @ 2025-12-14 22:45  complexor  阅读(11)  评论(0)    收藏  举报