[CF1043F] Make It One

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]=Ccntjijkdp[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值