HDU 5119. Happy Matt Friends
Happy Matt Friends
题意
给定长度为 \(n\) 的序列 \(a\) ,从中选择任意个数字(可以为 \(0\)) ,要求选出来的所有数字异或和不小于 \(m\) ,问有多少种方案?
分析
计数DP,设 \(f(i, j)\) 表示前 \(i\) 个数字异或和正好为 \(j\) 的方案数量。每一个元素都有选和不选两种选择。
有状态转移方程:
\[f(i, j) = f(i-1, j \bigoplus a_i) + f(i-1, j)
\]
由于状态很多,需要开滚动数组。
注意内层循环需要枚举所有可能的异或值,我们需要取到在正好取到 (1 << x) 这种形式,否则异或会越界!
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
const int M = 1 << 20;
int times;
int a[45];
int f[2][M]; // f(i, j) 表示前i个数字,正好异或值为j的所有方案数
void solve ()
{
int n, m; scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", a + i);
memset(f, 0, sizeof f);
f[0][0] = 1;
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < M; j ++ )
f[i % 2][j] = f[(i - 1) % 2][j ^ a[i]] + f[(i - 1) % 2][j];
long long ret = 0;
for (int i = m; i < M; i ++ ) ret += f[n % 2][i];
printf("Case #%d: %lld\n", ++times, ret);
}
signed main ()
{
int _; for (scanf("%d", &_); _--; ) solve();
return 0;
}