4197: [Noi2015]寿司晚宴
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 412 Solved: 279
[Submit][Status][Discuss]
Description
为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 1,2,3,…,n−1,其中第 i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 到 n)。
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。
Input
输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。
Output
输出一行包含 1 个整数,表示所求的方案模 p 的结果。
Sample Input
3 10000
Sample Output
9
HINT
2≤n≤500
0<p≤1000000000
Source
30分做法:
考虑对于每个质数状压DP,f[i][j]表示第一个人选取集合为i,第二个人选取集合为j的方案数。比较简单的DP。
100分做法:
对于大于$sqrt n$的质数,每个数至多包含1个这样的因子,所以可以对小于$sqrt n$的质数,这样的数至多有8个。
对于每个数,对其大于$sqrt n$的质因子分类,分别做DP即可。dp[i][j][2]表示第一个人选取集合为i,第二个人选取集合为j,质因子分给第i个人的方案数。
1 #include<cstdio> 2 #include<algorithm> 3 #define ll long long 4 using namespace std; 5 int f[1<<8][1<<8],n,mod,dp[1<<8][1<<8][2]; 6 int prime[]={2,3,5,7,11,13,17,19},tot,ans; 7 struct node{int p,S;}a[505]; 8 bool operator<(node x,node y){return x.p<y.p;} 9 inline void solve(int x) 10 { 11 int now=0; 12 for(int i=0;i<8;i++) 13 if(!(x%prime[i])) 14 { 15 now|=1<<i; 16 while(!(x%prime[i]))x/=prime[i]; 17 } 18 a[++tot]=(node){x,now}; 19 } 20 int main() 21 { 22 scanf("%d%d",&n,&mod); 23 f[0][0]=1; 24 for(int i=2;i<=n;i++)solve(i); 25 sort(a+1,a+tot+1); 26 for(int l=1,r;l<=tot;l=r+1) 27 { 28 for(r=l;r<tot&&a[r+1].p==a[r].p&&a[r].p!=1;r++); 29 for(int i=0;i<256;i++) 30 for(int j=0;j<256;j++) 31 dp[i][j][0]=dp[i][j][1]=f[i][j]; 32 for(int k=l;k<=r;k++) 33 { 34 for(int i=255;~i;i--) 35 { 36 int now=255-i; 37 for(int j=now;;j=(j-1)&now) 38 { 39 if((a[k].S&j)==0) 40 { 41 dp[i|a[k].S][j][0]+=dp[i][j][0]; 42 if(dp[i|a[k].S][j][0]>=mod) 43 dp[i|a[k].S][j][0]-=mod; 44 } 45 if((a[k].S&i)==0) 46 { 47 dp[i][j|a[k].S][1]+=dp[i][j][1]; 48 if(dp[i][j|a[k].S][1]>=mod) 49 dp[i][j|a[k].S][1]-=mod; 50 } 51 if(!j)break; 52 } 53 } 54 } 55 for(int i=0;i<256;i++) 56 for(int j=0;j<256;j++) 57 { 58 f[i][j]=dp[i][j][0]+dp[i][j][1]-f[i][j]; 59 if(f[i][j]>=mod)f[i][j]-=mod; 60 if(f[i][j]<0)f[i][j]+=mod; 61 } 62 } 63 for(int i=0;i<256;i++) 64 { 65 int now=255-i; 66 for(int j=now;;j=(j-1)&now) 67 { 68 ans+=f[i][j]; 69 if(ans>=mod)ans-=mod; 70 if(!j)break; 71 } 72 } 73 printf("%d ",ans); 74 }