[Noi2015]寿司晚宴

[Noi2015]寿司晚宴

题目描述

为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴。小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴。

在晚宴上,主办方为大家提供了n−1种不同的寿司,编号1,2,3,⋯,n-1,其中第种寿司的美味度为i+1(即寿司的美味度为从2到n)。

现在小G和小W希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小G品尝的寿司种类中存在一种美味度为x的寿司,小W品尝的寿司中存在一种美味度为y的寿司,而x与y不互质。

现在小G和小W希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数p取模)。注意一个人可以不吃任何寿司

思路

比较显然的结论是:当一个人选择一个数时,就相当于选择了它的质因数集。若用两个二进制数s1,s2表示两个人现在已经拥有哪些质因数时,一定满足s1 or s2 == 0.

有了这个结论,\(n<=30\)的情况已经可以解决了.状压\(dp\)即可。

随后,可以知道,若\(n<=500\),那么每个数最多只有一个大于等于\(\sqrt{n}\)的质因数,那么,只需要额外考虑含有这些“大质因数”的数即可。显然,这些质因数不能同时出现在两个人手中。那么,我们将\(dp\)转移稍微修改一下。

我们把所有大质因数相同的数放一起算。

\[f[0][bit],表示当前把数给小G(或不拿)的方案数 \]

\[f[1][bit],表示当前把数给小W(或不拿)的方案数 \]

转移和之前一样,只不过\(f[0]\)只转移给\(f[0]\),\(f[1]\)只转移给\(f[1]\).每当一个大质因数算完后,再去重(不拿的情况会被算到两次)。

若没有大质因数,则每算一次都去重即可。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=(l),i##R=(r);i<=i##R;i++)
#define DOR(i,r,l) for(int i=(r),i##L=(l);i>=i##L;i--)
#define loop(i,n) for(int i=0,i##R=(n);i<i##R;i++)
#define mms(a,x) memset(a,x,sizeof a)
using namespace std;
typedef long long ll;
template<typename A,typename B>inline void chkmax(A &x,const B y){if(x<y)x=y;}
template<typename A,typename B>inline void chkmin(A &x,const B y){if(x>y)x=y;}
const int N=505;
bool mm1;
int n;ll P,ans;
void Add(ll &x,ll y){
	x+=y;
	if(x>=P)x-=P;
}
void Mul(ll &x,ll y){
	x=x*y%P;
}
int pri[N],ct;
bool check(int x){
	for(int i=2;i*i<=x;i++)
		if(x%i==0)return false;
	return true;
}
ll f[3][1<<10][1<<10];
//f[2]为合并后总方案
struct node{
	int st,p;
	bool operator<(const node &A)const{
		return p<A.p;
	}
}A[N];
void solve(){
	for(int i=2;i*i<=500;i++)if(check(i))pri[ct++]=i;
	f[2][0][0]=1;
	FOR(i,2,n){
		int st=0,k=i;
		loop(j,ct)if(k%pri[j]==0){
			st|=(1<<j);
			while(k%pri[j]==0)k/=pri[j];//预处理大质因数(1则为没有)
		}
		A[i]=(node){st,k};
	}
	sort(A+2,A+n+1);//使大质因数相同的和一起算
	FOR(i,2,n){
		if(i==2||A[i].p!=A[i-1].p||A[i].p==1){//若开始计算新的大质因数,则将f[1]和f[0]设为当前答案
			DOR(j,(1<<ct)-1,0)DOR(k,(1<<ct)-1,0)
				f[0][j][k]=f[1][j][k]=f[2][j][k];
		}
		DOR(j,(1<<ct)-1,0)DOR(k,(1<<ct)-1,0){
			if(j&k)continue;
			if((j&A[i].st)==0)Add(f[0][j][k|A[i].st],f[0][j][k]);
			if((k&A[i].st)==0)Add(f[1][j|A[i].st][k],f[1][j][k]);
		}
		if(i==n||A[i].p==1||A[i].p!=A[i+1].p){
			DOR(j,(1<<ct)-1,0)DOR(k,(1<<ct)-1,0){
				if(j&k)continue;
				f[2][j][k]=f[0][j][k]+f[1][j][k]-f[2][j][k];
				f[2][j][k]=(f[2][j][k]%P+P)%P;
			}
		}
	}
	DOR(j,(1<<ct)-1,0)DOR(k,(1<<ct)-1,0)
		Add(ans,f[2][j][k]);
	printf("%lld\n",ans);
}
int main(){
	scanf("%d%lld",&n,&P);
	solve();
	return 0;
}
posted @ 2019-09-05 15:09  Hëinz  阅读(201)  评论(0编辑  收藏  举报