题意:
有n个数,从n个数中选出两个集合s和集合t,保证原序列中,集合s中的元素都在
集合t中元素的左边。且要求集合s中元素做抑或运算的值与集合t中元素做与运算的
值相等。问能选出多少种这样的集合s和t。
算法:
左右dp。
用dp[i][j]表示前i个数 做抑或运算得到j的方法数。最后一个值取不取到都不一定。
故为背包的问题。右边也是一样。
枚举时可能出现重复。枚举到第i个和枚举第i+1个可能重复。所以要枚举一个中间值。
这个中间值是归到s集的,因为抑或支持逆运算,而与是不支持的。
所以最后dp方程应该改为 前i个数一定包含第i个做抑或得到值j的方法数,即s[i][j]。
那么s[i][j] = dp[i-1][j^a[i]]。
右边也是做与运算,但要注意下标。
然后取long long应该在乘法之前,否则乘法运算后可能已经溢出,再做long long 就没意义了。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int mod = 1000000000+7;
typedef long long ll;
int a[1010];
int dp[1010][1025],dp1[1010][1025],s[1010][1025];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<1024;j++)
{
dp[i][j] = dp[i-1][j]+dp[i-1][j^a[i]];
if(dp[i][j]>=mod)
dp[i][j] -= mod;
}
for(int j=0;j<1024;j++)
s[i][j] = dp[i-1][j^a[i]];
}
memset(dp1,0,sizeof(dp1));
for(int i=n;i>=1;i--)
{
dp1[i][a[i]]++;
for(int j=0;j<1024;j++)
{
dp1[i][j&a[i]] = (dp1[i][j&a[i]]+dp1[i+1][j])%mod;
dp1[i][j] = (dp1[i][j]+dp1[i+1][j])%mod;
}
}
int cnt = 0;
for(int k=1;k<=n-1;k++)
{
for(int j=0;j<1024;j++)
{
cnt = (cnt+(ll)s[k][j]*dp1[k+1][j])%mod;
if(cnt>=mod) cnt-=mod;
}
}
printf("%d\n",cnt);
}
return 0;
}