题面
解法
思路是真的神仙
- 考虑当 n n n比较小的时候怎么做,因为质因子的个数不多,所以将是否取质因子的状态压成一个二进制数然后转移即可
- 然后发现 n ≤ 500 n≤500 n≤500,质因子个数还是比较多的
- 那么我们对于每一个数都分开考虑,可以发现最多只会有一个质因子 > n >\sqrt n >n
- 计算一下 n \sqrt n n在 22 22 22左右,在这个范围内的质数很少,只有8个
- 那么我们可以将这些质因子的取值状态压成一个二进制数,设 f [ i ] [ j ] f[i][j] f[i][j]表示第一个人选择了质因子集合 i i i,第二个人选择了质因子集合 j j j的方案数
- 然后做法可以说就比较显然了:对于每一个数先求出大于 n \sqrt n n的质因子,然后按照这个排序,将大于 n \sqrt n n的质因子相同的放入一组。对于同一组里的数,我们可以进行这样一个dp:设 f 1 [ i ] [ j ] f1[i][j] f1[i][j]表示这个因子给第一个人, f 2 [ i ] [ j ] f2[i][j] f2[i][j]表示这个因子给第二个人的方案数。转移十分显然,这里就不必再赘述了
- 考虑将同一组里的全部求解完毕之后怎么把答案合并到 f [ i ] [ j ] f[i][j] f[i][j]上,因为在计算的时候会重复计算两个人都不选择这个质因子的情况,所以 f [ i ] [ j ] = f 1 [ i ] [ j ] + f 2 [ i ] [ j ] − f [ i ] [ j ] f[i][j]=f1[i][j]+f2[i][j]-f[i][j] f[i][j]=f1[i][j]+f2[i][j]−f[i][j]
- 因为这里开的是类似于滚动数组的东西,所以在求解 f 1 , f 2 f1,f2 f1,f2的时候下标应该倒着枚举,类似于0/1背包
- 时间复杂度: O ( n 2 16 ) O(n2^{16}) O(n216)
代码
#include <bits/stdc++.h>
#define N 550
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int prime[9] = {0, 2, 3, 5, 7, 11, 13, 17, 19};
int f1[N][N], f2[N][N], ans[N][N];
struct Info {
int s, key;
bool operator < (const Info &a) const {return key < a.key;}
void divide(int x) {
for (int i = 1; i <= 8; i++) {
if (x % prime[i] != 0) continue;
s |= 1 << i - 1;
while (x % prime[i] == 0) x /= prime[i];
}
key = x;
}
} a[N];
int main() {
int n, Mod; read(n), read(Mod);
for (int i = 2; i <= n; i++) a[i].divide(i);
sort(a + 2, a + n + 1); ans[0][0] = 1;
for (int i = 2; i <= n; i++) {
if (a[i].key == 1 || a[i].key != a[i - 1].key || i == 2)
memcpy(f1, ans, sizeof(f1)), memcpy(f2, ans, sizeof(f2));
for (int j = 255; ~j; j--)
for (int k = 255; ~k; k--) {
if ((j & k) > 0) continue;
if ((k & a[i].s) == 0) f1[j | a[i].s][k] = (f1[j | a[i].s][k] + f1[j][k]) % Mod;
if ((j & a[i].s) == 0) f2[j][k | a[i].s] = (f2[j][k | a[i].s] + f2[j][k]) % Mod;
}
if (i == n || a[i].key == 1 || a[i].key != a[i + 1].key)
for (int j = 0; j <= 255; j++)
for (int k = 0; k <= 255; k++) {
if ((j & k) > 0) continue;
ans[j][k] = ((f1[j][k] + f2[j][k]) % Mod - ans[j][k] + Mod) % Mod;
}
}
int ret = 0;
for (int i = 0; i <= 255; i++)
for (int j = 0; j <= 255; j++)
if ((i & j) == 0) ret = (ret + ans[i][j]) % Mod;
cout << ret << "\n";
return 0;
}