【bzoj4197】【NOI2015】寿司晚宴

来源:互联网 发布:淘宝直播公司 编辑:程序博客网 时间:2024/04/27 06:28

题意

  有n1个数从2n,从中选出两个集合SU(可以为),要求对于xSyU,都有gcdxy=1,求方案总数(n500

解法

状压DP
  首先看到互质这一条件,可以想到利用质因子来判断
  很同意证明,对于一个数x,大于x的质因子至多有一个。假设存在两个及两个以上大于x的质因子:p1p2pn,那么显然有ni=1pix,因此矛盾,得证
  那么我们可以将每个数分解质因数,单独记录那个大于x的质因子,至于其他因子,我们可以用一个8位二进制数表示(因为满足x50022.3的质数只有8个),所以就可以设立DP的状态:
  设fij表示集合S的质因子包含情况为i,集合U的质因子包含情况为j的方案数,满足i&j=0gij0/1表示集合S的质因子包含情况为i,集合U的质因子包含情况为j,并且那个大于x的质因子在S/U之中的方案数
  fij=g[ij0]+gij1fij

复杂度

O(2828n

代码

#include<algorithm>#include<iostream>#include<cstdlib>#include<cstdio>#define Lint long long intusing namespace std;const int MAXN=(1<<8)+100;const int L=(1<<8)-1;struct node{    int s,x;    Lint pi;    bool operator < (const node &a) const    {        return pi<a.pi;    }}t[MAXN*2];Lint prime[8]={2,3,5,7,11,13,17,19};Lint f[MAXN][MAXN],g[MAXN][MAXN][2];Lint n,p,ans;void init(){    for(int i=L;i>=0;i--)   for(int j=L;j>=0;j--)   g[i][j][0]=g[i][j][1]=f[i][j];}void write(int x)   { if( !x )   cout<<x; while( x )   cout<<(x&1),x/=2; }int main(){    scanf("%lld%lld",&n,&p);    Lint x;    for(int i=2;i<=n;i++)    {        x=(Lint)i,t[i].x=i;        for(int j=0;j<=7 && prime[j]<=x;j++)            if( !(x%prime[j]) )            {                t[i].s|=(1<<j);                while( !(x%prime[j]) )   x/=prime[j];            }        if( x^1 )   t[i].pi=x;    }    sort( t+2,t+n+1 );    f[0][0]=1;    for(int i=2;i<=n;i++)    {        if( t[i].pi!=t[i-1].pi || !t[i].pi )   init();        for(int j=L;j>=0;j--)            for(int k=L;k>=0;k--)                if( !(j&k) )                {                    if( !(t[i].s&k) )   g[t[i].s|j][k][0]=(g[t[i].s|j][k][0]+g[j][k][0])%p;                    if( !(t[i].s&j) )   g[j][t[i].s|k][1]=(g[j][t[i].s|k][1]+g[j][k][1])%p;                }        if( t[i].pi!=t[i+1].pi || !t[i].pi )            for(int j=L;j>=0;j--)                for(int k=L;k>=0;k--)                    if( !(j&k) )   f[j][k]=(g[j][k][0]+g[j][k][1]-f[j][k])%p;    }    for(int i=L;i>=0;i--)        for(int j=L;j>=0;j--)        {            if( i&j )   continue ;            ans=(ans+f[i][j])%p;        }    printf("%lld\n",(ans+p)%p);    return 0;}