分析:
首先我们可以考虑一下简单的dp
把每一个数质因数分解,显然两个人在选择寿司的时候,如果小G已经有了质因数X,则小W就不能选择包含X的寿司了
我们就可以设计一个比较暴力的dp
f[i][x][y]
f
[
i
]
[
x
]
[
y
]
表示到第
i
i
个数,小G选择寿司的质因数状态为,小W选择寿司的质因数状态为
y
y
由于状态太多(95个素数),所以我们需要减少状态
500−−−√=22
500
=
22
考虑一个数最多有一个大于
500−−−√
500
的质因子,可以特殊考虑
而小于等于 500−−−√ 500 的质因子只有8个: {2,3,5,7,11,13,17,19} { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 }
小于等于
500−−−√
500
的质因子可以通过状压解决
而大于
500−−−√
500
的部分我们必须想办法解决冲突和重复计算的问题
将所有数包含的质因子处理出来,把每个数按照“大于
500−−−√
500
的质因子”排序,那么这个质因子相等的区间我们一起处理
显然这相等的一整个区间,必须分配给同一个人(或者两者都不选)
g[i][s1][s2][0/1] g [ i ] [ s 1 ] [ s 2 ] [ 0 / 1 ] 表示到第 i i 个数,第一个人选择的集合为,第二个人选择的集合为 s2 s 2 ,同时当前这个大于 500−−−√ 500 的质因子放入第一个人/第二个人的方案数
我们按照之前的暴力dp方法解决掉 g[i][s1[s2][0/1] g [ i ] [ s 1 [ s 2 ] [ 0 / 1 ] 的转移
f[i][s1][s2]=g[i][s1][s2][0]+g[i][s1][s2][1]−f[i][s1][s2]
f
[
i
]
[
s
1
]
[
s
2
]
=
g
[
i
]
[
s
1
]
[
s
2
]
[
0
]
+
g
[
i
]
[
s
1
]
[
s
2
]
[
1
]
−
f
[
i
]
[
s
1
]
[
s
2
]
表示这两种情况相加,但是因为这个质因子两个都不放的情况计算了两次,所以需要减掉一次
ps: p s : 对于状态转移,我们可以用01背包的方式优化掉第一维
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=503;
int sshu[8]={2,3,5,7,11,13,17,19};
struct node{
int S; //小于sqrt(500)的质因数集合
int prime; //大于sqrt(500)的质因数
};
node a[N];
int n;
ll p,f[N][N],g[N][N][2];
int cmp(const node &a,const node &b) {
return a.prime<b.prime;
}
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%sshu[j]==0) {
a[i].S|=(1<<j);
while (x%sshu[j]==0) x/=sshu[j];
}
a[i].prime=x;
}
sort(a+2,a+1+n,cmp); //按照大于sqrt(500)的质因数排序
f[0][0]=1;
ll ans=0;
for (int i=2;i<=n;i++)
{
//转移比较小的数或者是新的一个集合
if (i==2||a[i].prime==1||a[i].prime!=a[i-1].prime)
for (int j=0;j<(1<<8);j++)
for (int k=0;k<(1<<8);k++)
g[j][k][0]=g[j][k][1]=f[j][k];
for (int j=(1<<8)-1;j>=0;j--)
for (int k=(1<<8)-1;k>=0;k--) {
if (!(j&a[i].S)) //放入k
(g[j][k|a[i].S][1]+=g[j][k][1])%=p;
if (!(k&a[i].S)) //放入j
(g[j|a[i].S][k][0]+=g[j][k][0])%=p;
}
//只有转移新的一个集合 才需要复制到f数组
if (i==n||a[i].prime==1||a[i].prime!=a[i+1].prime)
for (int j=0;j<(1<<8);j++)
for (int k=0;k<(1<<8);k++)
f[j][k]=(g[j][k][0]+g[j][k][1]-f[j][k])%p;
}
for (int i=0;i<(1<<8);i++)
for (int j=0;j<(1<<8);j++)
if (!(i&j)) ans+=f[i][j];
printf("%lld\n",(ans%p+p)%p);
return 0;
}