BZOJ 4197: [Noi2015]寿司晚宴

状压<=sqrt(n)的所有质因数

对所有数分解质因数,大于sqrt(n)的质因数最多出现一次

包含相同大于sqrt(n)的质因数的数应该都归属于同一个人

按大于sqrt(n)的质因数排序

F[X][Y]表示两个人小于sqrt(n)的质因数的选取情况

G[0/1][X][Y]用于转移包含相同的大于sqrt(n)的质因子的数

精妙之处在于用状压然后分类避免了质因数之间相互影响的情况

#include<cstdio>
#include<algorithm>
using namespace std;
int n,mod,F[305][305],G[2][305][305];
int prime[8]={2,3,5,7,11,13,17,19};
struct node{
	int S,val;
}E[505];
bool cmp(node a,node b){
	return a.val<b.val;
}
int main(){
	scanf("%d%d",&n,&mod);
	for (int i=2; i<=n; i++){
		int x=i;
		for (int j=0; j<8; j++)
			if (x%prime[j]==0){
				E[i-1].S|=1<<j;
				while (x%prime[j]==0) x/=prime[j];
			}
		E[i-1].val=x;
	}
	n--;
	sort(E+1,E+n+1,cmp);
	F[0][0]=1;
	int N=8;
	for (int i=1; i<=n; i++){
		if (E[i].val==1 || E[i].val!=E[i-1].val){
			for (int X=0; X<(1<<N); X++)
				for (int Y=0; Y<(1<<N); Y++) 
					G[0][X][Y]=G[1][X][Y]=F[X][Y];
		}
		for (int X=(1<<N)-1; X>=0; X--)
			for (int Y=(1<<N)-1; Y>=0; Y--)
				if (!(X&Y)){
					if (!(E[i].S&Y)) (G[0][X|E[i].S][Y]+=G[0][X][Y])%=mod;
					if (!(E[i].S&X)) (G[1][X][Y|E[i].S]+=G[1][X][Y])%=mod;
				}
		if (E[i].val==1 || E[i].val!=E[i+1].val){
			for (int X=0; X<(1<<N); X++)
				for (int Y=0; Y<(1<<N); Y++)
					if (!(X&Y)) F[X][Y]=((G[0][X][Y]+G[1][X][Y]-F[X][Y])%mod+mod)%mod;
		}
	}
	int ans=0;
	for (int X=0; X<(1<<N); X++)
		for (int Y=0; Y<(1<<N); Y++)
			if (!(X&Y)) (ans+=F[X][Y])%=mod;
	printf("%d\n",ans);
	return 0;
}

  

posted @ 2018-10-29 20:39  ~Silent  阅读(127)  评论(0编辑  收藏  举报
Live2D