题目描述
https://codeforces.com/contest/1670/problem/F
简要题意:给定 $n,l,r,z$,求有多少个长度为 $n$ 的序列 $a_i$,满足 $l\le\sum_{i=1}^na_i\le r$ 且 $\bigoplus_{i=1}^na_i=z$,其中 $\oplus$ 为异或
$n\le 1000,1\le l,r \le 10^{18},z\le 10^{18}$
Solution
考虑数位 $dp$,我们令 $f[o][i]$ 表示填到了第 $o$ 位,第 $o$ 位最多能选 $i$ 个 $1$,注意到 $f[o][i]$ 向 $f[o-1][k]$ 转移时,能选的 $1$ 的个数为 $i\times 2$ 加上第 $o-1$ 位是否为 $1$,注意到我们一次最多只能选 $n$ 个 $1$,所以我们第二维对 $n$ 取 $min$ 即可
时间复杂度 $O(64n^2)$,注意并不需要使用记忆化搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <iostream> #define maxn 1010 #define ll long long using namespace std;
const int p = 1000000007; inline int add(int x, int y) { return (x += y) >= p ? x - p : x; }
int C[maxn][maxn]; void init_C(int n) { for (int i = 0; i <= n; ++i) C[i][0] = 1; for (int i = 1; i <= n; ++i) for (int j = 1; j <= i; ++j) C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]); }
const int N = 60;
int n; ll l, r, z;
int f[64][maxn]; int calc(ll d) { fill(f[0], f[0] + 64 * maxn, 0); f[60][0] = 1; for (int o = 60; o; --o) for (int i = 0; i <= n; ++i) { int ones = 2 * i + (d >> o - 1 & 1); for (int j = z >> o - 1 & 1; j <= min(n, ones); j += 2) f[o - 1][min(n, ones - j)] = add(f[o - 1][min(n, ones - j)], 1ll * C[n][j] * f[o][i] % p); } int res = 0; for (int i = 0; i <= n; ++i) res = add(res, f[0][i]); return res; }
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> l >> r >> z; init_C(n); cout << add(calc(r), p - calc(l - 1)) << "\n"; return 0; }
|