HDU

来源:互联网 发布:linux改名命令 编辑:程序博客网 时间:2024/06/05 02:07

题目意思是:给定 n 个数,求所有子集的异或和 大于等于 m 的子集个数有多少

首先想到的是枚举子集,但是 n 最大是 40 ,2 的 40 次方没法实现

然而给定 的每个数 a [ i ] 最大范围是 1e6 ,二进制下不会超过 19 位,所以任意多个 a [ i ] 进行异或,其answer 都是小于 2进制下 20位的这个数 ------ (2 << 20)

然后开滚动数组 dp [ i ] [ j ] 表示前 i 个数中,异或和为 j 的个数

注意:题目中又说可以不选,所以初始化的时候 dp [ 1 ] [ 0 ] = 1; dp [ 1 ] [ a[i] ] = 1;

递推式就很容易了,见代码中:


#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <string>#include <cmath>#include <set>#include <map>#include <stack>#include <queue>#include <ctype.h>#include <vector>#include <algorithm>#include <sstream>#define PI acos(-1.0)#define in freopen("in.txt", "r", stdin)#define out freopen("out.txt", "w", stdout)using namespace std;typedef long long ll;const int maxn = (1<<20) + 7, INF = 0x3f3f3f3f, mod = 1e9 + 7;int T, n, m;int a[50];ll dp[42][maxn];void init() {    scanf("%d %d", &n, &m);    for(int i = 1; i <= n; ++i)        scanf("%d", &a[i]);    memset(dp, 0, sizeof dp);}void solve() {    dp[1][0] = dp[1][a[1]] = 1;    for(int i = 1; i <= n; ++i) {        for(int j = 0; j < maxn; ++j) {            dp[i][j] += dp[i-1][j];            dp[i][j^a[i]] += dp[i-1][j];        }    }    ll ans = 0;    for(int i = m; i < maxn; ++i)        ans += dp[n][i];    printf("%lld\n", ans);}int main() {    scanf("%d", &T);    int kase = 1;    while(T--) {        init();        printf("Case #%d: ", kase++);        solve();    }    return 0;}


原创粉丝点击