状压(dp).
对于(30\%)的数据,(nleq 30),质因数的种类最多只有10个,所以可以压缩状态,设状态(sta)为第(i)位为集合中是否有第(i)个质因数。
设(f[i][s1][s2])表示当前考虑到第(i)个数,第一个集合状态为(s1),第二个集合状态为(s2)的方案数。
设当前枚举到的数状态为(k),显然有转移:(f[i][s1|k][s2]+=f[i-1][s1][s2])或(f[i][s1][s2|k]+=f[i-1][s1][s2]).
这种刷表转移,根据套路,可以逆循环省掉第一维,时间复杂度(O(n*2^{20})).
然后对于所有数据,上面的算法就不行了。但是看一眼数据范围:(nleq 500),还是很小。
可以发现500的范围最多只能有一个大于22的质数,而小于22的质数只有8个。于是先按这个大一点的质数排序,然后一个一个质数的处理。
设(f[i][s1][s2])表示当前做到第(i)个质数了,前8个质数的状态为(s1,s2)的方案数。注意没有比22大的质数的数算一类。
对于当前枚举到的质数(p),设(g[0/1][s1][s2])表示状态为(s1,s2),当前质数p给第一个/第二个人,且算上质数p及以前的质数的方案数,然后(g)的初值可以设为上一个质数的(f).这表示不含当前质数的方案数。然后(g)的转移可以按照30分的方法转移。
当枚举到含有质数p的最后一个数时,转移完(g)时可以利用(g)来更新(f):
[f[i][s1][s2]=g[0][s1][s2]+g[1][s1][s2]-f[i-1][s1][s2]
]
因为(g[0])和(g[1])都含有不选质数p的情况,所以要减去一个。
然后发现(f)的第一维还是可以省掉,时间复杂度(O(n*2^{16}))。
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
void print(int x) {
if(x<0) x=-x,print(x/10);
if(!x) return ;print(x/10);putchar((x%10)^48);
}void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
int n,f[300][300],g[300][300][2];
int pri[]={0,2,3,5,7,11,13,17,19};
struct node {
int p,s;
void init(int x) {
int num=x;
for(int i=1;i<=8;i++)
if(!(num%pri[i])) {
s|=1<<(i-1);
while(!(num%pri[i])) num/=pri[i];
}p=num;
}
bool operator < (const node x) const {return p<x.p;}
}a[1000];int mod;
void out(int x) {
char s[10];
//itoa(x,s,2);
printf("%s
",s);
}
int add(int x,int y) {x+=y;if(x>mod) x-=mod;if(x<0) x+=mod;return x;}
signed main() {
read(n),read(mod);for(int i=2;i<=n;i++) a[i].init(i);
//for(int i=2;i<=n;i++) out(a[i].s);
sort(a+2,a+n+1);f[0][0]=1;
for(int i=2;i<=n;i++) {
if(a[i].p!=a[i-1].p||a[i].p==1||i==2)
for(int s1=0;s1<=255;s1++)
for(int s2=0;s2<=255;s2++)
g[s1][s2][0]=g[s1][s2][1]=f[s1][s2]%mod;
for(int s1=255;~s1;s1--)
for(int s2=255;~s2;s2--) {
if(!(a[i].s&s1)) g[s1][s2|a[i].s][1]=(g[s1][s2|a[i].s][1]+g[s1][s2][1])%mod;
if(!(a[i].s&s2)) g[s1|a[i].s][s2][0]=(g[s1|a[i].s][s2][0]+g[s1][s2][0])%mod;
}
if(a[i].p!=a[i+1].p||a[i].p==1||i==n)
for(int s1=0;s1<=255;s1++)
for(int s2=0;s2<=255;s2++)
f[s1][s2]=((g[s1][s2][0]+g[s1][s2][1]-f[s1][s2])%mod+mod)%mod;
}
int ans=0;
for(int s1=0;s1<=255;s1++)
for(int s2=0;s2<=255;s2++)
if(!(s1&s2)) ans=(ans+f[s1][s2])%mod;
write(ans);
return 0;
}