hdu 5119 (类似于划分数的状态定义) (DP中的计数问题)
题目描述:求n个数中异或值大于m的方案数有多少个?
设状态f[i][j]代表前i个数异或值为j的方案数有f[i][j]种,那么对于j来说要么选第i个数与前面的i-1个数中的某些数构成j,f[i-1][j^a[i]]],要么不选第i个数,直接由前面的i-1个数构成j,f[i-1][j]; f[i][j]=f[i-1][j^a[i]] + f[i-1][j] ; 注意到j的取值范围为10^6约等于2^(20),所以n个数亦或的最大值最多为20个1。
亦或的特性 : 任何数与0相亦或不变 ,任何数与本身相亦或为0
设初值f[0][0]=1;
之前用背包那种滚动数组来写,但是会发现d[i][j]= d[i-1][j] + d[i-1][j-V[i]]; 背包方程中不选的话j-V[i]<=j 所以j从大到小遍历,这一行的值只与上一行的值有关,
然而这道题j^a[i]可能大于j,所以这一行的值不能保证只与上一行的值有关。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstring> #include <algorithm> #define SIZE 50 #define LL long long using namespace std; LL a[50]; LL ans[50][1148576]; void init() { memset(ans,0,sizeof(ans)); } int main() { //freopen("test.txt","r",stdin); int t; scanf("%d",&t); int cas = 1; while(t --) { LL n,m; scanf("%I64d%I64d",&n,&m); init(); for(int i = 1 ; i <= n ; i ++) { scanf("%I64d",&a[i]); //ans[a[i]] = 1; } LL all=pow(2,n); ans[0][0]=1; for(int i = 1; i <= n ; i ++) { for(int j = 0;j<1048576;j++) ans[i][j] = ans[i-1][j^a[i]]+ans[i-1][j]; } LL answer=0; for(int j=0;j<m;j++) answer+=ans[n][j]; printf("Case #%d: %I64d\n",cas ++,all-answer); } return 0; }