【题解】yyy loves Maths VII
\(\text{Solution:}\)
一个显然的状压 \(dp\) 是,设 \(f[S]\) 表示状态 \(S\) 中的数已经被选后的所有胜利方案数,那么最终的结果就是 \(f[2^n-1]\)
那么对于转移,我们直接枚举它的二进制下 \(1\) 的位置,它从 \(i-2^j,2^j\) 两个地方转移。
关于厄运数字,我们直接判断当前位置的 \(sum\) 是不是厄运数字。为了卡常数, \(sum\) 的维护要在转移中进行,这样可以少一个 \(2\) 的常数。
那么复杂度就是 \(O(n\cdot 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\n",f[(1<<n)-1]);
return 0;
}