题目大意
给出$2$到$n$共$n-1,nle 500$个数字,求从中选出两个集合使得从两个集合内各取任意一个数字互质的方案数。
简要题解
要满足题中的条件,其实就是要求两个集合中出现的质因子不同。注意到$n,nle 500$以内的数字,要么只存在一个大于$sqrt{n}$的质因子,要么可以由前$8$个质数分解。对于后者,拉出来用$8$位$bit$表示质因子情况压位DP出$f[i][j][k]$表示从前$i$个数字中取出两个集合内质因子状态分别为$j,k$的方案数;再考虑把前者加到DP中去,对于前者中的数,只需要确定把它放在哪个集合即可,而不需要去记录在状态中,因为我们一次性考虑完所有带这个大于$sqrt{n}$的质因子的数:要么不选,要么只能放到同一个集合中去,这样就可以减少状态同时避免了后效性。
想的时候确实考虑到所有的合数可以由前$8$个素数筛完,但没有进一步想到如何利用这个性质来简化状态。
然后就是喜闻乐见地打错下标WA了一发。。
1 #include <bits/stdc++.h> 2 using namespace std; 3 namespace my_header { 4 #define pb push_back 5 #define mp make_pair 6 #define pir pair<int, int> 7 #define vec vector<int> 8 #define pc putchar 9 #define clr(t) memset(t, 0, sizeof t) 10 #define pse(t, v) memset(t, v, sizeof t) 11 #define bl puts("") 12 #define wn(x) wr(x), bl 13 #define ws(x) wr(x), pc(' ') 14 const int INF = 0x3f3f3f3f; 15 typedef long long LL; 16 typedef double DB; 17 inline char gchar() { 18 char ret = getchar(); 19 for(; (ret == ' ' || ret == ' ' || ret == ' ') && ret != EOF; ret = getchar()); 20 return ret; } 21 template<class T> inline void fr(T &ret, char c = ' ', int flg = 1) { 22 for(c = getchar(); (c < '0' || '9' < c) && c != '-'; c = getchar()); 23 if (c == '-') { flg = -1; c = getchar(); } 24 for(ret = 0; '0' <= c && c <= '9'; c = getchar()) 25 ret = ret * 10 + c - '0'; 26 ret = ret * flg; } 27 inline int fr() { int t; fr(t); return t; } 28 template<class T> inline void fr(T&a, T&b) { fr(a), fr(b); } 29 template<class T> inline void fr(T&a, T&b, T&c) { fr(a), fr(b), fr(c); } 30 template<class T> inline char wr(T a, int b = 10, bool p = 1) { 31 return a < 0 ? pc('-'), wr(-a, b, 0) : (a == 0 ? (p ? pc('0') : p) : 32 (wr(a/b, b, 0), pc('0' + a % b))); 33 } 34 template<class T> inline void wt(T a) { wn(a); } 35 template<class T> inline void wt(T a, T b) { ws(a), wn(b); } 36 template<class T> inline void wt(T a, T b, T c) { ws(a), ws(b), wn(c); } 37 template<class T> inline void wt(T a, T b, T c, T d) { ws(a), ws(b), ws(c), wn(d); } 38 template<class T> inline T gcd(T a, T b) { 39 return b == 0 ? a : gcd(b, a % b); } 40 template<class T> inline T fpw(T b, T i, T _m, T r = 1) { 41 for(; i; i >>= 1, b = b * b % _m) 42 if(i & 1) r = r * b % _m; 43 return r; } 44 }; 45 using namespace my_header; 46 47 const int MAXN = 500 + 10; 48 const int PNUM = 8; 49 const int SNUM = 1 << 8; 50 const int PRI[] = {2, 3, 5, 7, 11, 13, 17, 19}; 51 int n, mod; 52 int fac[MAXN], rem[MAXN]; 53 int f[2][2][MAXN][MAXN], d[2][MAXN][MAXN], (*g)[MAXN]; 54 55 void add(int &x, int y) { 56 x += y; 57 if (x >= mod) 58 x -= mod; 59 } 60 61 int main() { 62 #ifdef lol 63 freopen("129.in", "r", stdin); 64 freopen("129.out", "w", stdout); 65 #endif 66 67 fr(n, mod); 68 for (int i = 2; i <= n; ++i) { 69 rem[i] = i; 70 for (int j = 0; j < PNUM; ++j) 71 if (rem[i] % PRI[j] == 0) { 72 fac[i] |= 1 << j; 73 while (rem[i] % PRI[j] == 0) 74 rem[i] /= PRI[j]; 75 } 76 } 77 int cur = 0, lst = 1; 78 d[cur][0][0] = 1; 79 for (int i = 2; i <= n; ++i) 80 if (rem[i] == 1) { 81 swap(cur, lst); 82 memcpy(d[cur], d[lst], sizeof d[cur]); 83 for (int j = 0; j < SNUM; ++j) 84 for (int k = 0; k < SNUM; ++k) { 85 if (!(k & fac[i])) 86 add(d[cur][j | fac[i]][k], d[lst][j][k]); 87 if (!(j & fac[i])) 88 add(d[cur][j][k | fac[i]], d[lst][j][k]); 89 } 90 } 91 g = d[cur]; 92 93 //for (int i = 0; i < 4; ++i) { 94 // for (int j = 0; j < 4; ++j) 95 // ws(g[i][j]); 96 // bl; 97 //} 98 99 for (int i = 2; i <= n; ++i) 100 if (rem[i] != 1) { 101 cur = 0, lst = 1; 102 memcpy(f[0][cur], g, sizeof f[0][cur]); 103 memcpy(f[1][cur], g, sizeof f[1][cur]); 104 for (int y = rem[i], x = y; x <= n; x += y) { 105 rem[x] = 1; 106 swap(cur, lst); 107 memset(f[0][cur], 0, sizeof f[0][cur]); 108 memset(f[1][cur], 0, sizeof f[1][cur]); 109 for (int j = 0; j < SNUM; ++j) 110 for (int k = 0; k < SNUM; ++k) { 111 add(f[0][cur][j][k], f[0][lst][j][k]); 112 add(f[1][cur][j][k], f[1][lst][j][k]); 113 if (!(k & fac[x])) 114 add(f[0][cur][j | fac[x]][k], f[0][lst][j][k]); 115 if (!(j & fac[x])) 116 add(f[1][cur][j][k | fac[x]], f[1][lst][j][k]); 117 } 118 } 119 for (int j = 0; j < SNUM; ++j) 120 for (int k = 0; k < SNUM; ++k) 121 g[j][k] = ((f[0][cur][j][k] + f[1][cur][j][k] - g[j][k]) % mod + mod) % mod; 122 } 123 int ans = 0; 124 for (int j = 0; j < SNUM; ++j) 125 for (int k = 0; k < SNUM; ++k) 126 add(ans, g[j][k]); 127 wt(ans); 128 129 return 0; 130 }