QOJ.ac

QOJ

Type: General Discussion

Status: Open

Posted by: Anonymous

Posted at:

Last updated:

Back to Problem

Proof of answer's upperbound is 21 and corresponding input

在这份题面的数据范围内,答案的最大可能值就是 (21),不只是接近上界。

1. 可以直接使用的数据

输入:

1
3839 461772685556582399

正确输出:

21

2. 为什么这组数据的答案确实是 21

我用了两份独立实现——C++ BFS 和 Python BFS——直接在原始整数状态 ((a,b)) 上搜索。每个状态完整枚举题面的两种合法操作,没有归一化、启发式转移或上界剪枝。

两份程序得到完全一致的结果:

  • 目标状态 ((0,0)) 首次出现在第 (21) 层,所以不存在 (20) 步或更短的方案。
  • 搜索至该层时,累计发现 2818 个不同状态。
  • 找到了一条实际可执行的 (21) 步路线。

记 (A) 表示对 (a) 操作,(B) 表示对 (b) 操作,一条最优路线是:

$$ \boxed{A^{10}\;B^4\;A^4\;B\;A\;B} $$

这里每次减去的都是当前的 (\gcd(a,b)),并非始终减去 (1)。路线的关键状态如下:

已执行步数 刚执行的操作 (a) (b)
0 初始状态 3839 461772685556582399
10 连续 (10) 次 (A) 3829 461772685556582399
14 连续 (4) 次 (B) 3829 461772685556582395
18 连续 (4) 次 (A) 3625 461772685556582395
19 (B) 3625 461772685556582250
20 (A) 0 461772685556582250
21 (B) 0 0

这条路线证明“至多 21 步”,完整 BFS 则证明“至少 21 步”。

3. 为什么任何合法数据都不可能超过 21

这部分不是抽样搜索,而是一个覆盖所有合法输入的上界递推。

先消去公因子

设最少操作次数为 (F(a,b))。由于整个操作过程可以等比例缩放,

$$ F(ga,gb)=F(a,b). $$

因此,只需要考虑互质状态 ((m,n)),其中 (1\le m\le n)。

定义 (U(m)):对于所有与 (m) 互质的 (n\ge m),都成立的操作次数上界。

当 (m=1) 时,先清零较小数,再清零另一个数:

$$ U(1)=2. $$

下面考虑 (m\ge2)。

策略一:先操作较小数

由于当前互质,第一步得到:

$$ (m-1,n). $$

设新的最大公约数为 (g),消去公因子后,较小数变成

$$ d=\frac{m-1}{g}, $$

它一定是 (m-1) 的正因子。因此,这种策略的总步数不超过

$$ 1+\max_{d\mid(m-1)}U(d). $$

策略二:连续操作较大数,直到出现非平凡公因子

设 (p(m)) 是 (m) 的最小质因子。

在第一次出现大于 (1) 的最大公约数之前,每次操作都将 (n) 减去 (1)。因为 (p(m)\nmid n),至多操作 (p(m)-1) 次,就一定会出现公因子:最迟在较大数变成 (p(m)) 的倍数时发生,也可能更早发生。

设此时公因子为 (g>1),消去公因子后,较小数变成

$$ d=\frac m g, $$

它是 (m) 的真因子。因此,这种策略的总步数不超过

$$ p(m)-1+\max_{\substack{d\mid m\\d<m}}U(d). $$

取两种策略中较好的一个

得到递推:

$$ \boxed{ U(m)= \min\left( 1+\max_{d\mid(m-1)}U(d),\; p(m)-1+\max_{\substack{d\mid m\\d<m}}U(d) \right) } $$

右侧所有下标都小于 (m),所以可以从小到大计算。由数学归纳法,每个 (U(m)) 都是有效上界。

完整计算 (m=1,$\ldots$,5000),得到:

$$ \boxed{\max_{1\le m\le5000}U(m)=21.} $$

注意,不需要证明每个 (U(m)) 都是精确的最坏答案。只要它们都是有效上界,且最大值为 (21),就已经证明所有合法输入的答案不超过 (21)。

下面是可直接复核这个上界的 C++17 代码,运行后输出 21:

#include <bits/stdc++.h>
using namespace std;

int main() {
    constexpr int N = 5000;

    // divisors[x] 按递增顺序存放 x 的全部正因子。
    vector<vector<int>> divisors(N + 1);
    for (int d = 1; d <= N; ++d) {
        for (int x = d; x <= N; x += d) {
            divisors[x].push_back(d);
        }
    }

    vector<int> U(N + 1);
    U[1] = 2;

    for (int m = 2; m <= N; ++m) {
        int previousDivisorMax = 0;
        for (int d : divisors[m - 1]) {
            previousDivisorMax = max(previousDivisorMax, U[d]);
        }

        int properDivisorMax = 0;
        for (int d : divisors[m]) {
            if (d < m) {
                properDivisorMax = max(properDivisorMax, U[d]);
            }
        }

        // 最小的大于 1 的因子,就是最小质因子。
        int p = divisors[m][1];

        U[m] = min(
            1 + previousDivisorMax,
            (p - 1) + properDivisorMax
        );
    }

    cout << *max_element(U.begin() + 1, U.end()) << '\n';
}

Comments

No comments yet.