[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;
} 

 

posted @ 2018-09-25 11:01  comld  阅读(201)  评论(0编辑  收藏  举报