[BZOJ4197][NOI2015]寿司晚宴

bzoj
luogu

description

\(2\)\(n\)中选出两个不相交子集使得这两个集合中的任一对元素互质。问总方案数模\(mod\)的结果。\(n\le500\)

sol

首先有\(30\%\)的部分分是\(n\le30\),考虑到\(30\)以内的质因数只有\(10\)个,因此可以状压\(dp\),即设\(f[s_1][s_2]\)表示第一个人选出的质因数集合为\(s_1\),第二个人选出的质因数集合为\(s_2\)的方案数。复杂度\(O(n2^{20})\)

对于\(100\%\)的数据\(n\le500\)。首先可以发现一个这样的性质:每个数中含有至多一个大于等于\(23\)的质因数,那么也就是说大于等于\(23\)的质因数是不可能产生冲突的。所以我们就只需要考虑前\(8\)个字符,然后对于每一个大于等于\(23\)的质因数,枚举是哪个人选择这个质因数,然后对应的转移就行了。

对于不含大于等于\(23\)的质因数的数,可以当做它含有一个其他数都不含有的质因数,然后按照上述的方式转移,可以避免一些讨论。

复杂度是\(O(n2^{16})\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define pi pair<int,int>
#define mk make_pair
#define fi first
#define se second
const int N = 505;
int p[]={2,3,5,7,11,13,17,19};
int n,mod,f[N][N],g[2][N][N],ans;
pi a[N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main(){
	scanf("%d%d",&n,&mod);
	for (int i=2;i<=n;++i){
		int x=i,y=0;
		for (int j=0;j<8;++j)
			while (x%p[j]==0) y|=1<<j,x/=p[j];
		a[i]=mk(x,y);
	}
	sort(a+2,a+n+1);f[0][0]=g[0][0][0]=g[1][0][0]=1;
	for (int t=2;t<=n;++t){
		for (int i=255;~i;--i)
			for (int j=255;~j;--j){
				if ((j&a[t].se)==0) add(g[0][i|a[t].se][j],g[0][i][j]);
				if ((i&a[t].se)==0) add(g[1][i][j|a[t].se],g[1][i][j]);
			}
		if (a[t].fi==1||a[t].fi!=a[t+1].fi){
			for (int i=0;i<256;++i)
				for (int j=0;j<256;++j)
					f[i][j]=((g[0][i][j]+g[1][i][j]-f[i][j])%mod+mod)%mod;
			memcpy(g[0],f,sizeof(g[0]));
			memcpy(g[1],f,sizeof(g[1]));
		}
	}
	for (int i=0;i<256;++i)
		for (int j=0;j<256;++j)
			if ((i&j)==0) add(ans,f[i][j]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-07-08 19:56  租酥雨  阅读(196)  评论(0编辑  收藏  举报