【题解】yyy loves Maths VII

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;
}
posted @ 2021-07-12 20:04  Refined_heart  阅读(23)  评论(0编辑  收藏  举报