题解及代码:
#include
#include
#include
using namespace std;
const int mod=1000000007;
typedef long long ll;
ll dp1[1004][2050];
ll dp2[1004][2050];
int c[1010];
void init()
{
memset(dp1,0,sizeof(dp1));
memset(dp2,0,sizeof(dp2));
}
int main()
{
int T,n,x;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
int ma=-1,t;
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
ma=max(ma,c[i]);
if(i==n) break;
dp1[i][c[i]]++; //记得每输入一个数,就要记录一下
t=min(ma*2,2048);
for(int j=0;j<=t;j++)
{
dp1[i][j]=(dp1[i][j]+dp1[i-1][j]+dp1[i-1][j^c[i]])%mod;
//printf("i:%d j:%d %d\n",i,j,dp1[i][j]);
}
}
//printf("\n");
for(int i=n;i>=2;i--)
{
dp2[i][c[i]]++; //同上,记录
for(int j=0;j<=t;j++)
{
dp2[i][j]+=dp2[i+1][j];
dp2[i][j&c[i]]=(dp2[i][j&c[i]]+dp2[i+1][j])%mod;
}
}
/*for(int i=n;i>=2;i--)
{
for(int j=0;j<=t;j++)
{
printf("i:%d j:%d %d\n",i,j,dp2[i][j]);
}
}
puts("");
*/
ll ans=0;
for(int i=2;i<=n;i++)
{
for(int j=0;j<=t;j++)
{
if(dp1[i-1][j]&&dp2[i][j])
ans=(ans+dp1[i-1][j]*(dp2[i][j]-dp2[i+1][j]))%mod; //应为会出现重复的情况
//所以dp2[i][j]-dp2[i+1][j]
//保证每次相乘的都是新出现的结果
// printf("i:%d j:%d %d\n",i,j,ans);
}
}
printf("%I64d\n",ans);
}
return 0;
}
/*
题目意思是给出一串数序列,问是否能利用左侧的部分数进行异或,右侧的数进行与,来得到相等的数。
由于题目中给出的数最大为1024,所以最后总得到的结果最大也不超过2048,所以我们可以开出一个数组
来记录当我们进行到某个数时,我们能得到什么数,且它的数目是多少。
异或的操作从左向右进行,与的操作从右向左进行,最后对照相乘就行了。
这里给出两个dp的推导公式:
1.异或:dp2[i][j&c[i]]=(dp2[i][j&c[i]]+dp2[i+1][j])%mod;
2.与:dp2[i][j]+=dp2[i+1][j]; dp2[i][j&c[i]]=(dp2[i][j&c[i]]+dp2[i+1][j])%mod;
*转载请注明出处,谢谢。
*/