#include<bits/stdc++.h> #define ri register int usingnamespace std;
constint N = 5e2 + 10; constint M = 1<<8; constint pr[8] = {2,3,5,7,11,13,17,19}; int n, Mod, dp[M][M]; int Mer1[M][M], Mer2[M][M]; // Merge after dealing with, to DP
voidread(int &ret){ ret = 0; char ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) { ret = (ret<<1) + (ret<<3) + (ch^48); ch = getchar(); } return ; }
structNumber { int value; int MaxiPrime; int OtherPrime; voiddealwith(){ // deal with possible primes int cur = value; for (ri i=0; i<8; ++i) { // choose the primes if (cur % pr[i]) continue; // can't be devided with no left OtherPrime |= (1<<i); while (!(cur % pr[i])) cur /= pr[i]; } if (cur > 1) MaxiPrime = cur; return ; }
voidinit(){ read(n), read(Mod); for (ri i=2; i<=n; ++i) a[i-1].value = i, a[i-1].dealwith(); sort(a+1, a+n), dp[0][0] = 1; return ; }
voidMerge(ri &x, ri y){ x = (x + y) % Mod; return ; }
intmain(){ init(); // predict: // Rolling Array, Back-to-Head Scan! for (ri i=1; i<n; ++i) { if (i == 1 || a[i].MaxiPrime ^ a[i-1].MaxiPrime || !a[i].MaxiPrime) memcpy(Mer1, dp, sizeof Mer1), memcpy(Mer2, dp, sizeof Mer2); // just copy from dp to Merge
for (ri j=M-1; j>=0; --j) for (ri k=M-1; k>=0; --k) { // enumarate each possible situation // which is of the two people if (j&k) continue; // has the same elements if (!(j&a[i].OtherPrime)) Merge(Mer1[j][k|a[i].OtherPrime], Mer1[j][k]); if (!(k&a[i].OtherPrime)) Merge(Mer2[j|a[i].OtherPrime][k], Mer2[j][k]); # 需要注意的就是这里,不要写反了 }
if (i == n-1 || a[i].MaxiPrime ^ a[i+1].MaxiPrime || !a[i].MaxiPrime) for (ri j=0; j<M; ++j) for (ri k=0; k<M; ++k) { if (j&k) continue; // has the same elements ri to = Mer1[j][k] + Mer2[j][k] - dp[j][k]; to %= Mod, dp[j][k] = to; dp[j][k] = (dp[j][k] + Mod) % Mod; } } ri ans = 0; for (ri i=0; i<M; ++i) for (ri j=0; j<M; ++j) if (!(i&j)) Merge(ans, dp[i][j]); cout << ans << endl; return0; }