1 条题解
-
1
题意:
给定一个长度为 的数组,选择它的一个子序列(不一定要连续的),问有多少种选法使得它们 AND 的值的二进制表示法中有 个 。
思路:
这个题就是一个简单的 DP,
设 表示选择到了第 个数字(但不一定是把前 个数字都选择了),所有被选择的数字的 AND 值等于 的方案数。
那么我可以不选择这个数字:,即与选择 个数字,数字的 AND 的值为 的方案数一样。
那么我们也可以选择这个数字:,即从前 个数得到的 与上一个 就有前 个数字得到的 。
当然,我们为什么一定要让第 个数字受到前面的数字的影响呢?我们可以另起炉灶!即 。
这就讨论完了所有情况。
归纳总结起来就是
$$\begin{cases} f_{i,j} = f_{i,j} + f_{i-1,j} \\ f_{i,j\& a_i} = f_{i,j\& a_i} + f_{i - 1,j} \\ f_{i, a_i}=1 \\ \end{cases} $$代码:
#include <bits/stdc++.h> using namespace std; const int N = 200010, mod = 1e9 + 7; int f[N][64]; int n, k; int a[N]; void solve() { cin >> n >> k; for (int i = 1; i <= n; i++) memset(f[i], 0, sizeof(f[i])); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { f[i][a[i]] = 1; for (int j = 0; j < 64; j++) { f[i][j] = (1ll * f[i][j] + f[i - 1][j]) % mod; f[i][j & a[i]] = (1ll * f[i][j & a[i]] + f[i - 1][j]) % mod; } } int res = 0; for (int i = 0; i < 64; i++) { int cnt = 0; for (int j = 0; j < 6; j++) { if (i >> j & 1) cnt++; } if (cnt == k) res = (1ll * res + f[n][i]) % mod; } cout << res << '\n'; } int main() { #ifdef DEBUG freopen("Test.in", "r", stdin); cout << "===================START===================" << endl; #endif ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) solve(); #ifdef DEBUG cout << "====================END====================" << endl; #endif return 0; }
- 1
信息
- ID
- 8737
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者