( ext{Solution:})
一个显然的状压 (dp) 是,设 (f[S]) 表示状态 (S) 中的数已经被选后的所有胜利方案数,那么最终的结果就是 (f[2^n-1])
那么对于转移,我们直接枚举它的二进制下 (1) 的位置,它从 (i-2^j,2^j) 两个地方转移。
关于厄运数字,我们直接判断当前位置的 (sum) 是不是厄运数字。为了卡常数, (sum) 的维护要在转移中进行,这样可以少一个 (2) 的常数。
那么复杂度就是 (O(ncdot 2^n)) 了。但是很难卡过去。
观察到我们每次只是取出一个二进制位下的 (1,) 而 lowbit(i)
这个函数的功能就是直接取出该数二进制下的第一个 (1) 的位置。
于是,我们可以用 lowbit
来优化转移。这样就可以轻松卡过去了。
主要收获:卡常数技巧以及 lowbit
的灵活运用。
#include<bits/stdc++.h>
#include<assert.h>
using namespace std;
const int mod=1e9+7;
const int dyx=(1<<30);
int n;
int m;
inline int lowbit(int x){return x&(-x);}
int u[2],f[1<<25],sum[1<<25];
int main(){
freopen("111.txt","r",stdin);
scanf("%d",&n);u[0]=-1;u[1]=-1;
for(int i=1;i<=n;++i)scanf("%d",&sum[1<<(i-1)]);
scanf("%d",&m);
for(int i=0;i<m;++i)scanf("%d",&u[i]);
f[0]=1;
for(int i=1;i<(1<<n);++i){
sum[i]=sum[i^lowbit(i)]+sum[lowbit(i)];
if(sum[i]==u[0]||sum[i]==u[1]){
continue;
}
for(int j=i;j;j-=lowbit(j)){
int pos=i-lowbit(j);
f[i]+=f[pos];
if(f[i]>=mod)f[i]-=mod;
}
}
printf("%d
",f[(1<<n)-1]);
return 0;
}