题意:给出一个序列,从从选出两个不为空的集合S T,S中的所有元素都是在T中的元素的左边,使得S集合中的数异或起来等于T集合中的数相与的数的选择方案数目。
思路:定义dpx[i][j]表示前i个数字能异或出j的值有多少中情况,dpa[i][j]表示后i个数能与出j的值有多少种情况。然后枚举两个集合的分界点,然后枚举异或的值来求解答案。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1007
#define mod 1000000007
typedef long long ll;
ll dpx[N][1<<10], dpa[N][1<<10], tmp[1<<10];
int num[N];
int main() {
int T, i, j, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (i = 1;i <= n;i++) scanf("%d", &num[i]);
memset(dpx, 0, sizeof(dpx));
memset(dpa, 0, sizeof(dpa));
dpx[0][0] = 1;
for (i = 1;i <= n;i++) {
for (j = 0;j < 1024;j++) {
dpx[i][j] = dpx[i-1][j]+dpx[i-1][j^num[i]];
dpx[i][j] %= mod;
}
}
for (i = 1;i <= n;i++) dpx[i][0]--, dpx[i][0] = (dpx[i][0]+mod)%mod;
for (i = n+1;i > 1;i--) {
for (j = 0;j < 1024;j++) {
dpa[i-1][j&num[i-1]] += dpa[i][j];
dpa[i-1][j&num[i-1]] %= mod;
dpa[i-1][j] += dpa[i][j];
dpa[i-1][j] %= mod;
}
dpa[i-1][num[i-1]]++;
dpa[i-1][num[i-1]] %= mod;
}
ll ans = 0;
for (i = 2;i <= n;i++) {
memset(tmp, 0, sizeof(tmp));
for (j = 0;j < 1024;j++) {
tmp[j&num[i]] += dpa[i+1][j];
tmp[j&num[i]] %= mod;
}
tmp[num[i]]++, tmp[num[i]] %= mod;
for (j = 0;j < 1024;j++) {
ans = (ans+dpx[i-1][j]*tmp[j]%mod)%mod;
}
}
printf("%I64d\n", ans);
}
}