题目链接(洛谷)
题目大意 有$[2,n]$一共$n-1$个数,甲乙两个人分别取子集$S,T$,要求不存在$x\in S,y\in T$,使得$\gcd(x,y) \neq 1$。求取子集的方案个数。
对于$30\%$的数据,$2\le n\le 30$;对于$100\%$的数据,$2\le n\le 500$
分析 暴力 两数互质,则两数没有相同质因数。对于$n\le 30$的数据,可能的质因数只有前$10$个,我们可以状压表示每个质因数的使用状态。
定义$f(i,s_1,s_2)$表示前$i$个数中,甲、乙选的集合的质因数状态分别为$s_1,s_2$。用$k$表示$i$的质因数状态,则有转移:
可以用滚动数组把第一维优化掉。复杂度$O(n\times 2^{20})$。
优化 当$n\le 500$时质数变多了,无法将它们全部状压。注意到一个数$n$最多有一个比$\sqrt{n}$大的质因子。对$n$质因数分解的时候,可以将这个大质数($\ge 23$)单独记下来,剩下的小质数状压。
先以大质数为关键字排序,把大质数相同的数放在一起。有同一大质数的数不可能同时出现在甲和乙的集合中。 所以我们将$f(s_1,s_2)$数组暂时分成两个,$g_1(s_1,s_2),g_2(s_1,s_2)$分别表示当前大质数只放进甲/乙的集合,甲乙小质数状态分别为$s_1,s_2$时的方案数。
$g_1,g_2$直接按照$30$分做法转移,当前大质数全部转移完后将答案合并到原来的$f$数组里,下一个大质数转移开始之前,将$g_1,g_2$附成当前$f$。
答案即$\sum f(s_1,s_2)~(s_1\operatorname{\&}s_2=0)$
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <bits/stdc++.h> #define ll long long using namespace std;int pri[] = {0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 };ll n, p, ans, f[300 ][300 ], g1[300 ][300 ], g2[300 ][300 ]; inline void mo (ll &x) { x = (x + p) % p; } struct node { ll big, sta; bool operator <(const node &x) const { return big < x.big; } } a[505 ]; void getSta () { for (ll i = 1 ; i < n; i++) { ll sta = 0 , tmp = i + 1 ; for (int j = 1 ; j <= 8 ; j++) { if (tmp % pri[j]) continue ; sta |= 1 << (j - 1 ); while (tmp % pri[j] == 0 ) tmp /= pri[j]; } a[i].big = tmp; a[i].sta = sta; } } void sol () { sort (a + 1 , a + n); f[0 ][0 ] = 1 ; for (ll i = 1 ; i < n; i++) { if (i == 1 || a[i].big != a[i - 1 ].big || a[i].big == 1 ) { memcpy (g1, f, sizeof (g1)); memcpy (g2, f, sizeof (g2)); } for (ll j = 255 ; j >= 0 ; j--) { for (ll k = 255 ; k >= 0 ; k--) { if (j & k) continue ; if ((a[i].sta & k) == 0 ) g1[j | a[i].sta][k] += g1[j][k], mo (g1[j | a[i].sta][k]); if ((a[i].sta & j) == 0 ) g2[j][k | a[i].sta] += g2[j][k], mo (g2[j][k | a[i].sta]); } } if (i == n - 1 || a[i].big != a[i + 1 ].big || a[i].big == 1 ) { for (ll j = 255 ; j >= 0 ; j--) { for (ll k = 255 ; k >= 0 ; k--) f[j][k] = g1[j][k] + g2[j][k] - f[j][k], mo (f[j][k]); } } } } void getAns () { for (ll i = 255 ; i >= 0 ; i--) { for (ll j = 255 ; j >= 0 ; j--) { if (i & j) continue ; ans += f[i][j], mo (ans); } } } int main () { ios::sync_with_stdio (false ); cin >> n >> p; getSta (); sol (); getAns (); cout << ans << endl; return 0 ; }