题解:#9209.[DAY01]角谷变换 审核通过

root 站长 2025-10-07 17:01:59 5

首先需要注意 IO 加速,这方面技巧可以查看公众号“三三信奥”的《[DAY01] 输入输出加速》内容。

子任务 1

把题目给的函数复制下来,对于每个询问直接调用函数输出即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXR = 1000000;
int Q, R;
int f(int x, int n)
{
    if (n == 0)
        return x;
    if (x % 2 == 0)
        return f(x / 2, n - 1);
    return f(min(3 * x + 1, R), n - 1);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> Q >> R;
    while (Q--)
    {
        int x, n;
        cin >> x >> n;
        cout << f(x, n) << "\n";
    }
    return 0;
}

子任务 2

算是一个偏误导的子任务,也确实是一个想不到正解的拿分子任务。

我们容易发现 的变化周期很明显:

这样就有了小范围暴力枚举,大范围通过周期快速计算的方法。

子任务 3

一个偏麻烦的做法

其实从角谷猜想这个基本结论容易发现,这里每个数必然会变到 或变到 ,因此只在处理 的变化路径后,预处理 范围内的变化链即可。

实际上因为我造数据偷懒,所以 的变化非常短,没有特意构造。

但实际上处理 范围内的变化链本身就有点麻烦。后面处理周期的细节也有点多。

优雅的倍增

显然变 步可以先变一半再变另一半,这题可以倍增处理。

#include <bits/stdc++.h>
using namespace std;
const int MAXR = 1000000;
int Q, R;
// f[i][j] 存 i 开始 2^j 变到了几。
int f[MAXR + 5][35];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> Q >> R;
    for (int i = 1; i <= R; i++)
    {
        if (i % 2 == 0)
            f[i][0] = i / 2;
        else
            f[i][0] = min(i * 3 + 1, R);
    }
    for (int j = 1; j <= 30; j++)
        for (int i = 1; i <= R; i++)
            f[i][j] = f[f[i][j - 1]][j - 1];
    while (Q--)
    {
        int x, n;
        cin >> x >> n;
        for (int j = 30; j >= 0 && n > 0; j--)
        {
            if ((1LL << j) <= n)
            {
                x = f[x][j];
                n -= (1LL << j);
            }
        }
        cout << x << "\n";
    }
    return 0;
}
{{ vote && vote.total.up }}