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 ≤ k i ≤ 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
分析:
题意:
第一行输入一个整数T,表示案例数,然后是T个案例。每个案例第一行输入两个数n和m,表示有n个幻数,从这n个数中取任意个数求异或,异或后的结果不小于m的方法有多少种?
解析:
想要做这道题,首先要知道异或是什么运算?异或:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。那么两个数异或后的结果最大也就为较大的那个数所有二进制数位全部变为1,这里数据的最大值就是106;而210>103;所以这里最大也不会大于220.
那么我们这道题就可以用DP做出来了!如果a、b两个值不相同,那么a ^ b ^ b=a;因为b和b的每个数位上的数都相同,那么异或的结果为0,0再与a异或,0的每个数位上都是0,如果0的某数位与1异或则为1,与0异或则为0,最终a的结果不变。
我们用dp[i][j]表示前i个数之间异或的结果为j;对于第i个数,我们可以取也可以不取,如果取,则结果可以是由j ^ book[i]与book[i]异或得到:dp[i-1][j^book[i]];
我们也可以不取,则为dp[i-1][j];
那么:
dp[i][j]=dp[i-1][j^book[i]]+dp[i-1][j];
这里我们看到了数组最多用到了dp[i]与dp[i-1],那么我们就可以用滚动数组,这样可以大大减少空间的消耗!
代码:
#include<iostream>
#include<cstdio>
#define N 55
#define M 1<<20
using namespace std;
int book[N];
long long dp[2][M];
int main()
{
int T,n,m,t=0,k;
long long sum;
scanf("%d",&T);
while(T--)
{
t++;
k=1;
sum=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&book[i]);
}
for(int i=0;i<M;i++)
{
dp[0][i]=dp[1][i]=0;
}
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
k=!k;
for(int j=0;j<M;j++)
{
dp[!k][j]=dp[k][j^book[i]]+dp[k][j];
}
}
for(int i=m;i<M;i++)
{
sum+=dp[!k][i];
}
printf("Case #%d: %lld\n",t,sum);
}
return 0;
}