HDU 3693 Math teacher's homework(数位dp)

Math teacher's homework

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 646    Accepted Submission(s): 248


 

Problem Description

Mr. Furion is a math teacher. His students are very lazy and they do not like to do their homework. One day, Mr. Furion decides to give them a special problem in order to see whether his students are talents in math or they are just too lazy to do their homework. The problem is:


Given an integer k, n integers m1,m2…mn, and a formula below: 

X1 xor X2 xor X3… xor Xn = k 

Please figure out that how many integral solutions of the formula can satisfy:

0<=Xi<=mi (i=1…n)

 

 

Input

There are at most 100 test cases.

The first line of each test case contains two integers n and k. The second line of each test contains n integers: m1,m2…mn. The meaning of n,k, m1,m2…mn are described above. (1<=n<=50,0<=k,m1,m2…mn<=2^31-1 )

The input is ended by “0 0”.

 

 

Output

You should output a integer for each test case, which is the number of solutions. As the number might be very large, you should only output the number modulo 1000000003.

 

 

Sample Input

 

11 2047 1024 512 256 128 64 32 16 8 4 2 1 10 2047 1024 512 256 128 64 32 16 8 4 2 0 0

 

 

Sample Output

 

1 0

题意:给你n(n<=50)个数ai(ai<=2^31-1),再给你一个数k(k<=2^31-1),求x1^x2^...^xn=k(0<=xi<=ai)的解的总数。

思路:

考虑数位dp。

dp[i][pre][len]表示到当前xi,最低位为第len位的前缀为pre的答案总数。

我们枚举ai为1的那些位(把xi的这一位赋为0,那么低位就可以随便取了),考虑xi与ai的最长公共前缀。

因此可得状态转移方程:

dp[i][pre][len]=\sum_{j=31}^{0}\left ( dp[i+1][pre\bigoplus cur][max(j,len)]*2^{min(j,len)} \right )

cur表示的是xi的所有可能前缀。j表示cur的前缀长度。

但是第二维的数组显然开不了那么大。

仔细观察dp的过程可以发现,如果i和len确定时,当前的pre除了第len位都是可以确定的,因此第二维就可以优化成2了。

代码:

#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
#define dep(i,a,b) for(register int i=(a);i>=(b);--i)
using namespace std;
const int maxn=55;
const ll mo=1000000003;
int n,m,k,mm;
int tmp,cnt,ans;
ll dp[maxn][maxn][2];
ll po[maxn];
int a[maxn];
ll dfs(int i,int pre,int len)
{
    int now=0,k;
    pre&=(~((1<<len)-1));
    if(i==n+1) return !pre;
    if(pre&(1<<len)) k=1;
    else k=0;
    if(dp[i][len][k]!=-1) return dp[i][len][k];
    ll sum=0;
    dep(j,31,0)
    if(a[i]&(1<<j)){
        sum+=dfs(i+1,pre^now,max(j,len))*po[min(j,len)]%mo;
        sum%=mo;
        now|=(1<<j);
    }
    dp[i][len][k]=sum;
    return dp[i][len][k];
}
int main()
{
    po[0]=1;
    rep(i,1,maxn-1)
    po[i]=(po[i-1]<<1)%mo;
    int T,cas=1;
    while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
    {
        memset(dp,-1,sizeof(dp));
        rep(i,1,n)
        {
            scanf("%d",&a[i]);
            a[i]++;
        }
        ll ans=dfs(1,m,0);
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值