思路:
注意到對於每個數,其 \(>19\) 的質因數最多隻有 \(1\) 個,稱為大質數;對於 \(\le 19\) 的質因數有 \(8\) 個,稱為小質數。
設第 \(i\) 個數的小質數集合為 \(h_i\)。
那麼考慮對於所有數按照大質數從小到大排序,那麼對於大質數相同的一段,只能放在兩個集合中的一個。
考慮狀態壓縮 \(dp\),定義 \(dp_{S_1,S_2}\) 表示對於取完 \(i\)(可以滾動陣列) 個數後第一/二個集合的小質數集合,\(f1_{S_1,S_2}\) 表示這一段的數都放在第一個集合的方案數,\(f2_{S_1,S_2}\) 表示這一段的數都放在第二個集合的方案數。
則狀態轉移方程為(首先要滿足 \(S_1\) 與 \(S_2\) 沒有交集,即 \(S_1 \operatorname{and} S_2 = 0\)):
\[f1_{S_1 \operatorname{or} data_i,S_2} += f1_{S_1,S_2} [S_2 \operatorname{and} h_i = 0]
\]
\[f2_{S_1,S_2 \operatorname{or} data_i} += f1_{S_1,S_2} [S_1 \operatorname{and} h_i = 0]
\]
\[dp_{S_1,S_2} = f1_{S_1,S_2} + f2_{S_1,S_2} - dp_{S_1,S_2}
\]
因為 \(f1\) 和 \(f2\) 都可以不取這一段的數,那麼 \(dp_{S_1,S_2}\) 就被算了兩次,減掉即可。
時間複雜度為 \(O(n2^{16})\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=505,M=8,S=(1ll<<M);
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
struct Node{
ll x;
ll data;
bool operator<(const Node&rhs)const{
return x<rhs.x;
}
}a[N];
ll n,x,ans,mod;
ll dp[S][S],f1[S][S],f2[S][S];
ll P[]={2,3,5,7,11,13,17,19};
bool End;
int main(){
n=read(),mod=read();
for(int i=2;i<=n;i++){
x=i;
for(int j=0;j<8;j++){
if(x%P[j]==0){
a[i].data|=(1ll<<j);
while(x%P[j]==0)
x/=P[j];
}
}
if(x!=1)
a[i].x=x;
}
dp[0][0]=1;
sort(a+2,a+n+1);
for(int i=2;i<=n;i++){
if(!a[i].x||a[i].x!=a[i-1].x){
for(int X=0;X<S;X++)
for(int Y=0;Y<S;Y++)
f1[X][Y]=f2[X][Y]=dp[X][Y];
}
for(int X=S-1;X>=0;X--){
for(int Y=S-1;Y>=0;Y--){
if(X&Y)
continue;
if((a[i].data&Y)==0)
f1[X|a[i].data][Y]=Add(f1[X|a[i].data][Y],f1[X][Y]);
if((a[i].data&X)==0)
f2[X][Y|a[i].data]=Add(f2[X][Y|a[i].data],f2[X][Y]);
}
}
if(i==n||a[i].x!=a[i+1].x||!a[i].x){
for(int X=0;X<S;X++)
for(int Y=0;Y<S;Y++)
dp[X][Y]=(f1[X][Y]+f2[X][Y]-dp[X][Y]+mod)%mod;
}
}
for(int X=0;X<S;X++)
for(int Y=0;Y<S;Y++)
ans=Add(ans,dp[X][Y]);
write(ans);
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}