网上对这题的分析寥寥无几,分析的更是惨不忍睹,让人望而生畏。
我参考的博客 他写的思路是有道理的,但不看几个小时是无法理解的。
我的想法:首先这题,你不可能像别的数位dp一样,真的去dfs写出了,因为它是个计算方案数的问题,并且可选的数有n个。
但它还是一个数位dp,为什么?因为它还是利用了枚举位的思想去解决。
首先第一个思想:它最后要求要异或值为k,我们可以转换一下思路,我们不盯着最后结果为k来思考问题,我们盯着一个全局的:它能异或组成任意一个数(竟然能组成任意一个数了,自然也就能组成k啦)。
第二个思想:怎么样定义状态方程? 可以这样考虑:dp[i][j][k](其中k只取0或1),表示,前i个数,对应的二进制位为:0到j-1位,能组成小于等于2^(j-1)里的任意数,且第j位为k(K==0或k==1)的方案数。
可能读起来比较拗口,给个例子解释一下,比如只有1个数 a=3,a=11(3的二进制):那么dp[1][1][0]=1,因为2^1位为1,所以,小于2的数都可以去,也就是说,2^(1-1)一下的任意数都可以组成,这样的方案只有1个,因为只有一个数a。
有了这个方程,其实我们可以推出如何求最后答案,ans+=dp[n][i][k的第i位的值为多少] ,什么意思?也就是说,前i-1位已经可以组成小于等于2^(i-1)任意数了,并且只有第i位和k的第i位也一样,那么这样的方案是可以组成k的。
第三个思想:状态转移方程:我们考虑由前i项的方案数来推i+1项的方案数,考虑第i+1项的每一位,只有含有1,说明在这一位之前的任意状态都是可达的(因为比它小的数都可以取)。那么我们再此时由dp[i][j][0/1]转移到dp[i+1][j][0/1]时,第i+1个数的前
k项,k<=j都是任意的。(因为已经满足可以组成任意一个数了)。
考虑dp[i][j][0/1]转移到dp[i+1][k][0/1],k>j,这个时候因为前j个已经满足任意取,第i+1项,只要提供j+1到k 的任意取就行,所以,前j项还是可以随便取。
第四个思想:有了前面的三个思想,大致的解题方法应该已经出来了。(其实你还是不能实现),因为细节还是太多。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=60;
const int mod=1000000003;
ll dp[maxn][maxn][2];
ll a[maxn];
int xo[maxn][maxn];
int n;
ll k;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ll one=1;
while(scanf("%d%lld",&n,&k)!=EOF&&(n+k))
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]++;
}
memset(dp,0,sizeof(dp));
for(int i=0;i<=31;i++)
{
if(a[1]&(one<<i))
{
dp[1][i][0]=1;
}
}
for(int i=0;i<=31;i++)
{
for(int j=1;j<=n;j++)
{
if(j==1)
{
xo[i][j]=(a[j]&(one<<i))==0?0:1;
}
else
{
xo[i][j]=xo[i][j-1]^((a[j]&(one<<i))==0)?0:1;
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=31;j++)
{
if(dp[i][j][0]||dp[i][j][1])
{
for(int k=0;k<=31;k++)
{
if(a[i+1]&(one<<k))
{
if(k<=j)
{
if(k==j||(a[i+1]&(one<<j))==0)//i+1位不会贡献1
{
dp[i+1][j][0]+=dp[i][j][0]*(one<<k)%mod;
dp[i+1][j][1]+=dp[i][j][1]*(one<<k)%mod;
}
else
{
dp[i+1][j][0]+=dp[i][j][1]*(one<<k)%mod;
dp[i+1][j][1]+=dp[i][j][0]*(one<<k)%mod;
}
dp[i+1][j][0]%=mod;
dp[i+1][j][1]%=mod;
}
else
{
dp[i+1][k][xo[k][i]]+=(dp[i][j][0]+dp[i][j][1])%mod*(one<<j)%mod;
dp[i+1][k][xo[k][i]]%=mod;
}
}
}
}
}
}
ll ans=0;
for(int i=31;i>=0;i--)
{
int tmp=(k&(one<<i))==0?0:1,has=0;
ans+=dp[n][i][tmp];
ans%=mod;
for(int j=1;j<=n;j++)
{
has^=(a[j]&(one<<i))==0?0:1;
}
if(has!=tmp) break;
//printf("%d...\n",i);
}
printf("%lld\n",ans);
}
return 0;
}