[NOI2015]寿司晚宴(状压dp)
为了庆祝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取模)。注意一个人可以不吃任何寿司。
Solution
题意:有1-n-1这些数,把他们分给两个人(可以不分),使得从两个人各自任意拿出一个数,它们都是互质的。
注意到n是500,范围内小质数较少,可以考虑状压小质数。
但是大质数怎么处理?
我们可以设计状态为dp[i][j]表示做到了第i个大质数,当前小质数的选择情况为j时的方案数。
因为每个大质数只能给一个人,所以我们dp的阶段就是每个大质数。
最后统计答案时要注意,我们把都没选的情况考虑了两次,把多余的部分减去就可以了。
Code
#include<iostream> #include<cstdio> #include<algorithm> #define N 509 #define R register using namespace std; int prime[N],mdiv[N],n,k,size,ma; long long g[2][1<<9][1<<9],f[1<<9][1<<9],mod,ans; struct aaa { int a,b; }ji[N]; bool cmp(aaa a,aaa b) { return a.b<b.b; } int main() { scanf("%d%lld",&n,&mod); for(R int i=2;i<=n;++i) { if(!mdiv[i])mdiv[i]=i,prime[++prime[0]]=i; for(R int j=1;j<=prime[0]&&((k=prime[j]*i)<=n);++j) { mdiv[k]=prime[j]; if(i%prime[j]==0)break; } } size=min(prime[0],9); for(R int i=2;i<=n;++i){ int tmp=i; for(R int j=1;j<=size;++j) if(!(tmp%prime[j])) { while(!(tmp%prime[j]))tmp/=prime[j]; ji[i].a|=(1<<j-1); } ji[i].b=tmp; } sort(ji+2,ji+n+1,cmp); ma=(1<<size)-1; f[0][0]=1; for(R int i=2;i<=n;++i){ if((ji[i].b==1)||(ji[i].b!=ji[i-1].b)){ for(R int j=0;j<=ma;++j) for(R int k=0;k<=ma;++k) g[0][j][k]=g[1][j][k]=f[j][k]; } for(R int j=ma;j>=0;--j) for(R int k=ma;k>=0;--k) if(!(j&k)){ if(!(ji[i].a&j))(g[0][j][k|ji[i].a]+=g[0][j][k])%=mod; if(!(ji[i].a&k))(g[1][j|ji[i].a][k]+=g[1][j][k])%=mod; } if((ji[i].b==1)||(ji[i].b!=ji[i+1].b)) for(R int j=0;j<=ma;++j) for(R int k=0;k<=ma;++k) if(!(j&k)) f[j][k]=(g[0][j][k]+g[1][j][k]-f[j][k]+mod)%mod; } for(R int i=0;i<=ma;++i) for(R int j=0;j<=ma;++j) if(!(i&j)) (ans+=f[i][j])%=mod; cout<<ans; return 0; }