【数位DP】【P4317】花神的数论题
Description
给定 (n),求 (n) 以内所有正整数二进制下 (1) 的个数的乘积,答案对 (10^7 + 7) 取模
Limitations
(1 leq n leq 10^{15})
Solution
这是我见过最简单的数位 DP,然而我还是翻车了……
考虑 (1) 最多有 (O(log n)) 个,因此 (1) 的个数是可以枚举的。
枚举求有 (k) 个 (1) 的数的个数,设 (f_{i, j}) 是考虑前 (i) 位,剩下的位置还需要填 (j) 个 (1) 且不顶上界的方案数。转移显然:
[f_{i,j} = f_{i-1,j+1} f_{i-1,j}
]
分别对应第 (i) 位填 (0) 和填 (1)。
顶上界的情况记一下已经有几个 (1) 就可以更新了。
因为只考虑 (1) 的个数,所以前导 (0) 对答案没有贡献,不需要做特殊处理。
需要注意的是,(10^7 + 7) 并不是一个质数,(10^7 + 7 ~=~941 imes 10627),因此在 DP 的时候并不能模 (p - 1),因为他的欧拉函数并不是减一简单。事实上并不需要取模,因为方案数不会超过 (n),因此直接计算即可。
Code
#include <cstdio>
#include <cstring>
const int maxn = 64;
const int MOD = 10000007;
ll n;
int cnt, upc = -1;
ll ans = 1;
ll frog[maxn][maxn];
int MU[maxn];
ll calc(const int x);
ll mpow(const int x, ll y);
int main() {
freopen("1.in", "r", stdin);
qr(n);
do cnt += (MU[++upc] = n & 1); while (n >>= 1);
for (int i = 1; i < 60; ++i) {
ans = ans * mpow(i, calc(i)) % MOD;
}
ans = ans * cnt % MOD;
qw(ans, '
', true);
return 0;
}
ll calc(const int x) {
int cnt = x - 1;
memset(frog, 0, sizeof frog);
frog[upc][x] = 1;
for (int i = upc - 1; ~i; --i) {
int di = i + 1;
for (int j = 0; j <= x; ++j) {
frog[i][j] = frog[di][j + 1] + frog[di][j];
}
if (MU[i] && ~cnt) {
++frog[i][cnt--];
}
}
return frog[0][0];
}
ll mpow(const int x, ll y) {
ll _ret = 1, _tmp = x;
while (y) {
if (y & 1) {
_ret = _ret * _tmp % MOD;
}
_tmp = _tmp * _tmp % MOD;
y >>= 1;
}
return _ret;
}
Summary
神tm (10^7+7) 竟然不是个质数