题解:[ICPC 2021 Seoul R] Squid Game
不妨设 \(X\le Y\le Z\),目标状态是 \(X=0\)。操作次数上限不大,尝试考虑每次让 \(X\) 成比例减少(例如折半)。
设 \(a=\frac{Y}{X},b=\frac{Z}{X}\)。当 \(a\) 为正整数时考虑以下流程:
- 若 \(a\) 为偶数,将 \(Z\) 往 \(X\) 倒,那么 \(Z\leftarrow Z-X,X\leftarrow 2X\),故 \(a\leftarrow\frac{a}{2},b\leftarrow\frac{b-1}{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;
}

浙公网安备 33010602011771号