33dai三十三天备考计划训练第一天 [DAY01] D0624角谷变换 题解
题目传送门
戳这里
题面
33DAI 规定了一个上限正整数 \(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);
}
33DAI 希望你帮他算算 \(Q\) 个 函数值。
即算出\(Q\)个\(f(x_i,n_i)\)
\(1 \leq Q,R \leq 10^6\)
\(1 \leq x_i \leq R\)
\(1 \leq n_i \leq 10^9\)
思路
如果使用暴力,时间复杂度将会达到\(Qn\),显然不行。
这题可以利用\(\color{orange}{倍增}\)来做。
因为一次性变换\(n\)次,显然是可以拆成先变\(a_1\)次,然后变\(a_2\)次,...,最后变\(a_k\)次(\(\displaystyle{\sum_{i=1}^{k} a_i = n}\)) ,也就是说一次性变换\(n\) 次,可以拆成多次总共变换\(n\)次。
那么我们可以利用倍增在\(O(R\log 10^9)\)的时间复杂度内做出来:
- 将\(i\)变换\(2^j\)次后会变成哪个数字。
因为\(R\)是不变的,所以我们在\(Q\)次之前做完。
那么接下来,我们可以把一次性变换\(n\)次拆成变换多次,每次变换\(2^k\)次(\(0 \leq k \leq \log_2 n\)) ,求如何拆可以看\(n\)的二进制,因为二进制的每一位都是代表一个2的整数次幂。
那么这题就做好了,时间复杂度\((R\log 10^9) + Q\)
代码
#include <bits/stdc++.h>
using namespace std;
int f[1000001][31]; // 倍增数组
int main()
{
int Q, R;
cin >> Q >> R;
for (int i = 1; i <= R; i++)
{
if (i % 2 == 0) f[i][0] = i / 2; // 预处理出i变一次
else f[i][0] = min(i * 3 + 1, R); // 同上
}
for (int j = 1; (1 << j) <= 1e9; j++)
{
for (int i = 1; i <= R; i++)
{
f[i][j] = f[f[i][j - 1]][j - 1]; // 倍增求i变2^j次。
}
}
while (Q--)
{
int ans = 0;
int x, n;
cin >> x >> n;
ans = x;
for (int j = 30; j >= 0 && n > 0; j--)
{
if ((1 << j) <= n) // 从n中拆出2^j
{
ans = f[ans][j]; // 变换2^j次
n -= (1 << j); // 记得减去
}
}
cout << ans << '\n'; // 输出
}
return 0;
}
完结撒花~~~~

浙公网安备 33010602011771号