1 条题解
-
0
自动搬运
来自洛谷,原作者为

wangbinfeng
今天搞完大概就永远不会碰 OI 了,大家祝好!搬运于
2025-08-24 22:58:32,当前版本为作者最后更新于2024-08-08 08:55:43,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
已被打回两次,烦请告知具体不合规的位置,以便作者进行修改。
- 题解意义:这道题的原题空间限制是 30000KiB,所以必须用 ID-DFS。但是 luogu 里这道题的空间限制是 512.00MiB,所以用 BFS 的空间复杂度是没有问题的。鉴于这道题可以被作为是 ID-DFS 的模板题,建议还是跟着其他题解学一下 ID-DFS。那么为什么会有这个题解呢?因为我为了验证 BFS 可以通过本题,尝试了长达 天,所以本题解分享方法,以免后来人重复浪费时间。
- 题意简述:有两个变量,其初值分别为 和 ,请问这两个变量最少经过多少次乘或除操作,使得至少一个变量的值成为 。
- 必备公式:$x^a\times x^b=x^{a+b},\frac{x^a}{x^b}=x^{a-b},x^0=1$。
首先可以发现变量 其实没啥用,因为乘除操作完全可以转换为对其指数加减操作,所以题目可以转化为两个数 经过任意多操作得到 。因为 ,所以搜索即可。 对于状态 ,可以转移到 $ \begin{cases} (x,x+x)\\(x,y+y)\\(x+x,y)\\(y+y,y)\\(x,x+y)\\(x+y,y)\\(x,\lvert x-y\rvert)\\(\lvert x-y\rvert,y) \end{cases}$ 共计 种状态,搜索一下。
设答案为 ,则时间复杂度约为 ,经测试 ,跑所有状态显然不可能完成。容易想到记忆化,不幸的是还是超时。
考虑到令 ,则 ,那么他们凑出来的数将一定也只能是 的倍数(也可以把 搞成零然后再找,但显然更劣)。那么如果发现若 ,那么就可以直接剪枝。
现在 ID-DFS 已经可以 了,但是 BFS 还是有可能会 ,考虑继续卡常。由于每个人卡常的方法不尽相同,这里仅介绍我本人的卡常方法:
- 手写队列,不要用 STL 的
queue,经测试可以节省 300 ms。 - 手写
gcd,不要用 STL 的__gcd。 - 用
bitset或bool数组,不要用 STL 的map/unordered_map。
总结一下就是少用或不用 STL。
代码如下:
#include <bits/stdc++.h> using namespace std; int p, cnt; const int maxp = 2e4 + 9; bitset<maxp * maxp * 5> vis; const int M = 4e7 + 5, P = 4e7; struct Node { unsigned short x, y, dep; } v[M]; int l = 1, r = 0; #define min(a, b) (a < b ? a : b) #define max(a, b) (a > b ? a : b) #define ctz __builtin_ctz int az, bz, z, t; inline int gcd(int a, int b) { if (!a || !b) return 0; az = ctz(a), bz = ctz(b), z = (az > bz) ? bz : az, t; b >>= bz; while (a) a >>= az, t = a - b, az = ctz(t), b = min(a, b), a = t < 0 ? -t : t; return b << z; } int x, y, dep; inline int bfs() { Node now = {1, 0, 0}; for (v[++r] = now, (r == P) && (r = 0); 1; l++, (l == P + 1) && (l = 1)) { cnt++; x = max(v[l].x, v[l].y), y = min(v[l].x, v[l].y), dep = v[l].dep; if (x == p || y == p) { cout << dep; return dep; } if (x > p * 2) continue; if (vis[x * p * 2 + y]) continue; if (gcd(x, y) && p % gcd(x, y)) continue; vis[x * p * 2 + y] = true; now.x = x * 2, now.y = y, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x * 2, now.y = x, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x, now.y = y * 2, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = y, now.y = y * 2, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x + y, now.y = x, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x + y, now.y = y, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x - y, now.y = x, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); now.x = x - y, now.y = y, now.dep = dep + 1, v[++r] = now, (r == P) && (r = 0); } } signed main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); cin >> p; bfs(); }
- 后记:
经过我本人的测试,其实 ID-DFS 和 BFS 的遍历节点数相近,为同一个数量级,且 BFS 遍历的节点数甚至会更小(UPD:经过第二次测试,发现结果与第一次测试结论不符,等待最终确定结果),不清楚为什么经过卡常的 BFS 算法会比 ID-DFS 慢了 倍(对于第 #11 个测试点),希望有大佬可以告知原因。 - 鸣谢:感谢 @C20252323tzy 帮我卡常,题解中的代码也是这位大佬修改的,在此表示万分的感谢。
- 附录:关于代码运行时间的测试(在这个链接进行测试,仅供参考):
测试点 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 输入数据 1 2 5 10 100 1000 10000 11451 19997 20000 输出答案 0 1 3 4 8 12 16 17 18 17 ID-DFS 访问节点数量 1 3 12 14 356 51927 3139830 1408618 1764168 8069256 ID-DFS 代码运行时长 3ms 4ms 3ms 4ms 17ms 15ms 19ms 38ms BFS 访问节点数量 1 2 44 102 2790 129328 6321222 10676462 24174998 16792582 BFS 代码运行时长 3ms 6ms 149ms 308ms 766ms 422ms
- 1
信息
- ID
- 10104
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者