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

lalaouye
星河滚烫,你是人间理想。搬运于
2025-08-24 22:38:18,当前版本为作者最后更新于2025-08-19 17:30:26,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
是个美难题。
第一个想法是假设 或 为 该怎么做。我的想法是拆位去做,若 ,则每个数位的贡献是 ,但是发现这并没有什么卵用,这道题的关键在于如何处理二三进制之间的关系。不过按位处理是必需的,因为这道题一个数的贡献显然可以按位拆贡献,因为位数和在指数就能直接加了。
我尝试设 表示二进制到 位三进制到 位,且二进制数位和为 ,三进制数位和为 ,是否卡上界的方案数,但是发现这根本没有办法转移,因为进制之间的关系太复杂了,我们貌似只能考虑对其中一种数位进行 dp,三进制的位数更少,且转移二进制更为方便,我们考虑选择对三进制进行 dp。
设 表示考虑完了位数大于 的三进制位,当前二进制数为 ,是否卡上界的方案数,最后在 时统计答案。
但是这样跑起来比二进制还慢。但是我们注意到关于这种进制数位的题,随便优化一个个位置就是指数级别的优化。我们发现对于 ,不考虑进位的话只会对后 个数位产生影响,设其为 ,因为之后最终的 只会在 中出现。总之一个思想,根据可转移的范围去优化状态,我们设 表示考虑完高于 的进制位,当前值模 的值为 ,是否进位,是否顶上界的答案。
不太熟这种 trick 的人可能有点难以理解,实际上就是将原本的 按 划分到 个等价类里,它们是可以完成转移的。
转移可以根据我们钦定的进位,和加的值以及后继状态是否钦定进位去算固定的转移系数。相当于是说,首先枚举当前位是几,然后枚举后继是否进位,看加起来后看当前能否进位,也就是加起来的数是否 。可以的话,转移系数就只跟确定位置 的个数有关了。
但是这样没做完,我们忙活半天这个复杂度还是劣完了。我们不难感受到 越大三进制位的选择越少, 越小二进制位的选择越少,而进制为的变化带来的力量是巨大的,所以考虑根号分治,让 的时候直接暴搜, 的时候记忆化,这样复杂度就变成 ,我的 取的 。
这题还有一些其它做法,总之都是利用了位的贡献可拆然后去搞类似分治的东西,这是一个很好的思想。
代码:
#include <bits/stdc++.h> #define rep(i, l, r) for (int i (l); i <= (r); ++ i) #define rrp(i, l, r) for (int i (r); i >= (l); -- i) #define eb emplace_back using namespace std; #define pii pair <int, int> #define inf 1000000000ll #define ls (p << 1) #define rs (ls | 1) #define fi first #define se second constexpr int N = 1e7 + 5, M = 60 + 5, P = 998244353; typedef long long ll; typedef unsigned long long ull; inline ll rd () { ll x = 0, f = 1; char ch = getchar (); while (! isdigit (ch)) { if (ch == '-') f = -1; ch = getchar (); } while (isdigit (ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); } return x * f; } int qpow (int x, int y) { int ret (1); for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P; return ret; } int t[N], * o, * f[13][2]; ll ef[M], py[M], p3[M], n, x, y, z; ll pw[M]; int m; int dfs (int step, ll now, bool go, bool up) { if (step == -1) return ! go; if (step <= 12 && ! up && f[step][go][now] > -1) return f[step][go][now]; ll ret (0); int lim = up ? (n / p3[step] % 3) : 2; ll cur (1); rep (i, 0, lim) { if (i > 0) cur = cur * pw[step] % P * z % P; ll tmp (now + p3[step] * i); rep (j, 0, 1) { ll val (step ? ef[step - 1] : 1); ll nxt (tmp + j * val); if ((nxt >= ef[step]) == go) { nxt -= ef[step] * go; ret += py[__builtin_popcountll (nxt / val)] * cur % P * dfs (step - 1, nxt % val, j, up & (i == lim)); } } } ret %= P; if (step <= 12 && ! up) f[step][go][now] = ret; return ret; } int32_t main () { n = rd (), x = rd (), y = rd (), z = rd (); p3[0] = 1, py[0] = 1; ll c (1); while (c <= n) c *= 3, ++ m; pw[0] = x; rep (i, 1, m) p3[i] = p3[i - 1] * 3, pw[i] = pw[i - 1] * pw[i - 1] % P * pw[i - 1] % P; rep (i, 1, M - 1) py[i] = py[i - 1] * y % P; o = t; rep (i, 0, m) { ef[i] = 1ll << (int) ceil (log2 (3) * (i + 1)); if (i <= 12) { f[i][0] = o, o += ef[i], f[i][1] = o, o += ef[i]; rep (j, 0, ef[i] - 1) f[i][0][j] = f[i][1][j] = -1; } } cout << (dfs (m, 0, 0, 1) - 1 + P) % P; }
- 1
信息
- ID
- 7697
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者