题目链接:2014多校四-1005
CLJ大大所说的送分题,硬是想了好久好久。。。。。orz
给定n个数,从中选出S、T两个区间,要求保证S中所有数都在T中所有数的左侧。对S中的所有数做异或操作,对T中的所有数做位与操作,问使得两个结果相同的选法一共有多少种。
解题思路:DP,DP[i][j]表示选到i位置为止,异或值为j有多少种情况。然后依据右端取得j的情况数求最终结果
/*********************************************************************
FileName: 1005.cpp
Author: wing
Created Time: 2014年07月31日 星期四 12时51分14秒
*********************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mod 1000000007
#define FFF 1005
int n;
int a[FFF];
long long dp[FFF][1024];//长度为i,异或值为j的有多少种情况
long long sum[FFF][1024];//选用i位置的数,相与得j的有多少种情况
//直接开long long 不然会炸int
int main()
{
int keng;
scanf("%d",&keng);
while(keng--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
dp[1][a[1]]=1;
for(int i=2;i<=n;i++)
{
dp[i][a[i]]+=1;//之前的数都不选用,只用i位置的数的情况
for(int j=0;j<1024;j++)
{
dp[i][j]+=dp[i-1][j];//i位置的数不选用,得到异或值为j的情况
dp[i][j]%=mod;
dp[i][(j^a[i])]+=dp[i-1][j];//选用了i位置,得到异或值为j^a[i]的情况
dp[i][(j^a[i])]%=mod;
}
}
//debug
/* for(int i=1;i<=4;i++)
{
for(int j=0;j<4;j++)
cout<<dp[i][j]<<' ';
cout<<endl;
}cout<<endl;
*/
memset(sum,0,sizeof(sum));
sum[n][a[n]]=1;
long long ans=0;
ans+=dp[n-1][a[n]];
for(int i=n-1;i>1;i--)
{
sum[i][a[i]]+=1;
for(int j=0;j<1024;j++)
{
sum[i][(j&a[i])]+=sum[i+1][j];//选用了a[i]的情况
sum[i][(j&a[i])]%=mod;
}
for(int j=0;j<1024;j++)
{
ans=(ans+sum[i][j]*dp[i-1][j]%mod)%mod;//为保证不重复,计算时只考虑选用了a[i]的情况
sum[i][j]+=sum[i+1][j];
sum[i][j]%=mod;
}
}
//debug
/* for(int i=1;i<=4;i++)
{
for(int j=0;j<10;j++)
cout<<sum[i][j]<<' ';
cout<<endl;
}*/
cout<<ans<<endl;
}
return 0;
}
P.S. 计数数组要开long long,开int的时候炸了一次,第N次在long long上吃亏了