我觉得这个DP挺难的。。。然而这只是lydrainbowcat学长幻灯片上的第一题……
明天考试要GG。
题意:
给你一个序列,让你选出两个集合S和T。保证S里的数都在T里的数的左边。求一共有多少个集合满足S的异或所得等于T的与的所得,并mod一个大素数。
思路:
上课讲的DP,可惜我写了一晚上。。。。。。
首先我们可以枚举断点。i
这样就把序列分成了两部分。
前半部分是集合S
可以进行DP
j表示前i个数xor得j的方案数
f[i][j]=(f[i-1][j]+f[i-1][j*a[i]])%mod
(上一个状态和a[i]进行异或操作)
后半部分是集合T
也可以进行DP
j表示进行与操作得j的方案数
d[i][j]=d[i+1][j]
(显然,如果上一个状态可以到j,这个状态也可以)
d[i][j]=d[i+1][j],d[i][j&a[i]]=(d[i+1][j]+d[i][j&a[i]])%mod;
(上一个状态和a[i]进行位与操作)
所以 ans=((long long)f[i-1][j^a[i]]*d[i+1][j]+ans)%mod;
(要有强制类型转换)
// by SiriusRen
#include <bits/stdc++.h>
#define F for(int j=0;j<1024;j++)
using namespace std;
int cases,n,a[1001],f[1001][1025],d[1001][1025],mod=1e9+7,ans;
int main(){
scanf("%d",&cases);
while(cases--){
memset(f,0,sizeof(f)),memset(d,0,sizeof(d)),ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),d[i][a[i]]++;
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<1024;j++)f[i][j]=(f[i-1][j^a[i]]+f[i-1][j])%mod;
for(int i=n-1;i>=1;i--){
F d[i][j]=d[i+1][j],d[i][j&a[i]]=(d[i+1][j]+d[i][j&a[i]])%mod;
d[i][a[i]]++;
F ans=((long long)f[i-1][j^a[i]]*d[i+1][j]+ans)%mod;
}
printf("%d
",ans);
}
}
一不小心还刷了Code Length的第一。