hdu 5119 (类似于划分数的状态定义) (DP中的计数问题)

题目描述:求n个数中异或值大于m的方案数有多少个?

设状态f[i][j]代表前i个数异或值为j的方案数有f[i][j]种,那么对于j来说要么选第i个数与前面的i-1个数中的某些数构成jf[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个数亦或的最大值最多为201

亦或的特性 : 任何数与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;
}

 

posted on 2015-10-12 18:46  爱装逼的书呆子  阅读(345)  评论(1编辑  收藏  举报

导航