http://acm.hdu.edu.cn/showproblem.php?pid=4901
给定一个序列,要求选出两个集合,S和T,要求S中选中的元素的下标都要小于T中元素的下标。并且说S中元素的异或和要等于T中元素取且的和。
利用dp分左右两边处理 令f[i][j]表示从左到i位置且一定选取a[i]的情况下异或值为j的方案数,g[i][j]同理‘
注意最后计数时需要防止重复计数,所以对每个数组下标i,ans += f[i-1][j] * g[i+1][j^a[i]];
#include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <queue> #include <stack> #include <iostream> #include <algorithm> using namespace std; #define RD(x) scanf("%d",&x) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define clr0(x) memset(x,0,sizeof(x)) typedef long long LL; const int maxn = 1e5+5; int a[2050]; LL f[2050][2050],g[2050][2050]; #define mod 1000000007 int n,m; int main() { int _; RD(_); while (_--){ RD(n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); clr0(f),clr0(g); for (int i=n;i>=1;i--){ for (int j=0;j<1024;j++) g[i][j]=(g[i][j]+g[i+1][j])%mod,g[i][a[i] & j]=(g[i][a[i] & j]+g[i+1][j])%mod; g[i][a[i]] =(g[i][a[i]]+1)%mod; } LL ans=0; for (int i=1;i<n;i++){ for (int j=0;j<1024;j++){ int x = a[i]^j; ans=(ans + (1LL)*f[i-1][j]*g[i+1][x])%mod; } ans = (ans + g[i+1][a[i]])%mod; for (int j=0;j<1024;j++) f[i][j]=(f[i][j] + f[i-1][j])%mod,f[i][a[i] ^ j]=(f[i][a[i] ^ j]+f[i-1][j])%mod; f[i][a[i]]=(f[i][a[i]]+1)%mod; } printf("%I64d\n",ans); } return 0; }