当前位置: 代码迷 >> 综合 >> 【bzoj4197】【NOI2015】寿司晚宴
  详细解决方案

【bzoj4197】【NOI2015】寿司晚宴

热度:35   发布时间:2023-12-05 12:41:04.0

题意

??有n?1个数从2n,从中选出两个集合S U (可以为?),要求对于 ?xS?yU ,都有gcdxy=1,求方案总数(n500

解法

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

复杂度

O(28?28?n

代码

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Lint long long int
using 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;
}