HDU5119-Happy Matt Friends(动归,情况和)

题目链接

Problem Description

Matt has N friends. They are playing a game together.
Each of Matt’s friends has a magic number. In the game, Matt selects some (could be zero) of his friends. If the xor (exclusive-or) sum of the selected friends’magic numbers is no less than M , Matt wins.
Matt wants to know the number of ways to win. 

Input

The first line contains only one integer T , which indicates the number of test cases.
For each test case, the first line contains two integers N, M (1 ≤ N ≤ 40, 0 ≤ M ≤ 10^{6}).
In the second line, there are N integers ki (0 ≤ ki ≤ 10^{6}), indicating the i-th friend’s magic number.

Output

For each test case, output a single line “Case #x: y”, where x is the case number (starting from 1) and y indicates the number of ways where Matt can win.

Sample Input

2

3 2

1 2 3

3 3

1 2 3

Sample Output

Case #1: 4

Case #2: 2

Hint

In the first sample, Matt can win by selecting:

friend with number 1 and friend with number 2. The xor sum is 3.

friend with number 1 and friend with number 3. The xor sum is 2.

friend with number 2. The xor sum is 2.

friend with number 3. The xor sum is 3. Hence, the answer is 4.


题目大意

有n个数,取出几个数的算出他们的异或的和,求大于等于m的异或和有多少种情况。

用dp[i][j]表示用前n个数,他们之间组合的异或结果为j的情况个数。dp[i][j] = dp[i-1][j^a[i]] + dp[i-1][j] ,新添加一个数,异或为j的情况分为两种:不要这个数的情况dp[i-1][j],要这个数的情况dp[i-1][j^a[i]],两种情况合起来就是dp[i][j]。

dp[0][0]=1,这个初始化可以理解为0个数异或为0的情况的个数为1(感觉不太对)。其实按常理说应该把n个数初始化为1,dp[1][a[i]] = 1,很多dp求情况和的问题的初始化都是一个简单的dp[0][0] = 1,因为在i=1的时候,做的就是dp[1][a[i]] = 1。本质是一样的,而且写法更加简约。

关于 Max = 1<<20,数最大是10^{6}10^{3}可以看做是1024即2^{10},那么最大就应该是2^{20},用位运算符表示就是1<<20,这些数的异或和最大最大无非是全写1。

关于使用long long存sum,考虑最坏的情况,m=0,那么所有的情况都符合,所有组合加起来就是2^{n},n最大为40,int最大就是20亿,显然已经超过了。


#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int Max = 1<<20;
int dp[45][Max],a[45];
int n,m;

void init()
{
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<Max;j++){
            dp[i][j] = dp[i-1][j^a[i]] + dp[i-1][j];
        }
    }
}
int main()
{
    int T;scanf("%d",&T);
    for(int k=1;k<=T;k++)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        init();
        ll sum=0;
        for(int i=m;i<Max;i++)
            sum += dp[n][i];
        printf("Case #%d: %lld\n",k,sum);
    }
    return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值