题目描述
为了庆祝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取模)。注意一个人可以不吃任何寿司。
输入输出格式
输入格式:
从文件dinner.in中读入数据。
输入文件的第1行包含2个正整数n,p中间用单个空格隔开,表示共有n种寿司,最终和谐的方案数要对p取模。
输出格式:
输出到文件dinner.out中。
输出一行包含1个整数,表示所求的方案模p的结果。
输入输出样例
输入样例#1:
3 10000
输出样例#1:
9
输入样例#2:
4 10000
输出样例#2:
21
输入样例#3:
100 100000000
输出样例#3:
3107203
说明
n<=500 p<=109
n
<=
500
p
<=
10
9
分析:
一种合法的选法,那么他们的积互质,也就是他们的质因数分解没有交集。
但是
500
500
个里的质数比较多,无法直接状压。我们知道,一个数
x
x
大于的质因数最多只有一个,而
500
500
内的小于
n−−√
n
质因数只有8个,这样就可以状压了。
我们记录一下一个数大于
n−−√
n
的质因数具体是哪个,然后依据这个排序。对于大质数相同的,只能给其中一个人选,设两个数组
f1
f
1
和
f2
f
2
表示第一个人和第二个人选的状态,然后合并就可以,但是同时不选这个状态算重,要减掉这个状态的初始值。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
const int maxn=507;
const int prime[8]={2,3,5,7,11,13,17,19};
using namespace std;
int n;
LL p;
LL f[maxn][maxn],f1[maxn][maxn],f2[maxn][maxn];
struct rec{
int s;
int x;
}a[maxn];
bool cmp(rec a,rec b)
{
return a.x<b.x;
}
int main()
{
scanf("%d%lld",&n,&p);
for (int i=2;i<=n;i++)
{
int x=i;
for (int j=0;j<8;j++)
{
if (x%prime[j]==0)
{
a[i].s|=1<<j;
while (x%prime[j]==0) x/=prime[j];
}
}
a[i].x=x;
}
sort(a+2,a+n+1,cmp);
int s=1<<8;
f[0][0]=1;
int g=0;
for (int i=2;i<=n;i++)
{
if ((a[i].x==1) || (a[i].x!=a[i-1].x))
{
for (int j=0;j<s;j++)
{
for (int k=0;k<s;k++)
{
f1[j][k]=f[j][k];
f2[j][k]=f[j][k];
}
}
}
for (int j=s;j>=0;j--)
{
for (int k=s;k>=0;k--)
{
if ((k&a[i].s)==0) f1[j|a[i].s][k]=(f1[j|a[i].s][k]+f1[j][k])%p;
if ((j&a[i].s)==0) f2[j][k|a[i].s]=(f2[j][k|a[i].s]+f2[j][k])%p;
}
}
if ((a[i+1].x==1) || (a[i].x!=a[i+1].x))
{
for (int j=0;j<s;j++)
{
for (int k=0;k<s;k++)
{
f[j][k]=(f1[j][k]+f2[j][k]+p-f[j][k])%p;
}
}
}
}
LL ans=0;
for (int i=0;i<s;i++)
{
for (int j=0;j<s;j++)
{
if ((i&j)==0) ans=(ans+f[i][j])%p;
}
}
printf("%lld",ans);
}