洛谷 7976 「Stoi2033」园游会 题解
思路
先证明一下打表找出的规律.
引理:记 的三进制表达中 的个数为 ,有
在我校 MO 爷的帮助下给出的证明:
设满足 的 有 个,满足 的 有 个,即证明 .
考虑对 应用 Lucas 定理,设 的三进制表达分别为 ,有 .记 分别为 等于 时 构成的集合.
考虑计算 .要使 , 需要满足下列条件:
- 由于 中的位对乘积的贡献只能为 ,那么有 ,.
- 由于 ,有 是偶数.
枚举有多少 使得 ,有
同理可得
那么有
这不是我们二项式定理的题目吗!于是有
得证.
然后就可以枚举 ,数位 DP 计算 的数中有几个满足三进制表达中 的个数为 .
代码
卡了下常,写得很丑.
#include <cstdio>
#include <cstring>
#include <algorithm>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using u64 = unsigned long long;
inline u64 rd() {
u64 x = 0; int c = getchar();
while (((c - '0') | ('9' - c)) < 0) c = getchar();
while (((c - '0') | ('9' - c)) > 0)
x = x * 10 + c - '0', c = getchar();
return x;
}
const u64 P = 1732073999;
const int N = 40;
int nn[N + 5], len;
u64 pw[N + 5];
u64 f[N + 5][N + 5][2];
u64 dfs(int i, int ct, bool rmx) {
if (i == len + 1) return ct == 0;
if (f[i][ct][rmx] != 0xffffffffffffffff) return f[i][ct][rmx];
u64 res = 0;
for (int cur = 0, li = (rmx ? nn[i] : 2); cur <= li; cur++) {
int nc = ct - (cur == 1);
if (nc < 0) continue;
int nxt = dfs(i + 1, nc, rmx && cur == nn[i]);
res += nxt;
}
return f[i][ct][rmx] = res % P;
}
void solve() {
memset(f, 0xff, sizeof(f));
u64 n = rd(); len = 0;
while (n) nn[++len] = n % 3, n /= 3;
std::reverse(nn + 1, nn + 1 + len);
u64 ans = 0;
for (int i = 0; i <= len; i++) {
ans += dfs(1, i, 1) * pw[i] % P;
}
printf("%llu\n", ans % P);
}
int main() {
pw[0] = 1; for (int i = 1; i <= N; i++) pw[i] = pw[i - 1] * 2 % P;
int T = rd(); rd();
while (T--) solve();
return 0;
}