Make It One
题意:
题意很简单,给你一个长度为
n
n
n的数组
a
a
a,让你求最小的子集,使得他们的
g
c
d
=
1
gcd=1
gcd=1,输出最小子集的长度,没有则输出-1。
分析:
这类题还是很经典的,我们先观察它的数据范围是
3
×
1
0
5
3 \times 10^5
3×105,所以我们素数最多前7个就可以了。因为前7个素数相乘就已经超过了那个范围。
我们设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示取
i
i
i个数,
g
c
d
gcd
gcd为
j
j
j的集合最小数。
然后考虑状态转移方程。
d
p
[
i
]
[
j
]
=
C
c
n
t
j
i
−
∑
j
∣
k
d
p
[
i
]
[
k
]
dp[i][j] = C_{cnt_j}^{i} - \sum_{j|k}dp[i][k]
dp[i][j]=Ccntji−∑j∣kdp[i][k]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
const int MOD = 1e9 + 7;
ll fac[maxn], inv[maxn];
ll qpow(ll a, ll b, ll mod = MOD) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(int n, int m) {
if (n < m) return 0;
return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
void init() {
fac[0] = 1;
for (int i = 1; i <= maxn - 5; i++) fac[i] = fac[i - 1] * i % MOD;
inv[maxn - 5] = qpow(fac[maxn - 5], MOD - 2);
for (int i = maxn - 6; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
}
int n, cnt[maxn], dp[10][maxn];
int main() {
init();
scanf("%d", &n);
for (int i = 1, x; i <= n; i++) {
scanf("%d", &x);
cnt[x]++;
if (x == 1) return puts("1"), 0;
}
for (int i = 1; i <= maxn - 5; i++) {
for (int j = i + i; j <= maxn - 5; j += i) {
cnt[i] += cnt[j]; // 倍数
}
}
for (int i = 2; i < 10; i++) {
for (int j = maxn - 5; j; j--) {
dp[i][j] = C(cnt[j], i);
for (int k = j + j; k <= maxn - 5; k += j) {
dp[i][j] = (dp[i][j] - dp[i][k] + MOD) % MOD;
}
}
if (dp[i][1] > 0) return printf("%d\n", i), 0;
}
puts("-1");
return 0;
}