Codeforces Round #519 by Botan Investments F. Make It One(容斥+组合数学)

题目链接

题意

给出长度为 n n n的正整数序列,要求找出一个最小的子集,使得子集中的数的gcd等于1。没有的话输出-1,否则输出子集大小。序列中的数 0 ≤ a i ≤ 300000 0 \leq a_i \leq 300000 0ai300000

题解

暴力的做法就是从1开始枚举子集的大小,不同的子集个数 2 n 2^n 2n,肯定要超时。我们可以设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示集合大小为 i i i且集合 g c d gcd gcd等于 j j j的集合个数。当 d p [ i ] [ 1 ] > 0 dp[i][1]>0 dp[i][1]>0时,说明存在集合大小为 i i i的且其 g c d = 1 gcd=1 gcd=1

这里可以采取容斥来解决, d p [ i ] [ j ] = C ( i , c n t j ) − ∑ k = 0 n d p [ i ] [ k ] , j ∣ k 。 dp[i][j] = C(i,cnt_j)-\sum_{k=0}^{n}dp[i][k], j|k。 dp[i][j]=C(i,cntj)k=0ndp[i][k],jk

c n t j : cnt_j: cntj:序列中能被 j j j整除的数的个数。
C ( i , c n t j ) C(i,cnt_j) C(i,cntj):在序列中能被 j j j整除的数中选 i i i个。

为什么这么减呢?因为 C ( i , c n t j ) 选 出 来 的 数 的 集 合 C(i,cnt_j)选出来的数的集合 C(i,cntj)可能包括了能被 2 j , 3 j , . . , k j 2j,3j,..,kj 2j,3j,..,kj整除的集合。

这里 i i i的大小最大不超过7,因为要保证所有数互质,因为6个数不互质,7个数一定互质,那么每个数必须包含6个质因子。举个例子来说, 10 ,6 ,15 其质因子组成是 (2,5), (2,3), (3,5),每当gcd两个,都会消去不同质因子,比如 g c d [ ( 2 , 5 ) , ( 2 , 3 ) ] = 2 gcd[(2,5),(2,3)] = 2 gcd[(2,5),(2,3)]=2,所以如果7个数要够消的话,那么一个数要有6个质因子,每和一个数gcd就消去一个。

构造一下7个数的情况。
[ 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ] , [ 2 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ] , [ 2 ∗ 3 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ] , [ 2 ∗ 3 ∗ 5 ∗ 11 ∗ 13 ∗ 17 ] , [ 2 ∗ 3 ∗ 5 ∗ 7 ∗ 13 ∗ 17 ] , [ 2 ∗ 3 ∗ 5 ∗ 7 ∗ 13 ∗ 15 ] , [ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ] [2*3*5*7*11*13],[2*5*7*11*13*17],[2*3*7*11*13*17],[2*3*5*11*13*17],[2*3*5*7*13*17],[2*3*5*7*13*15],[3*5*7*11*13*17] [23571113],[257111317],[237111317],[235111317],[23571317],[23571315],[357111317]

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 3e5+5;
const int N = 3e5;
const int mod = 1e9+7;

ll dp[maxn];
int fen[maxn], mu[maxn];
ll pow_mod(ll x, ll n) {
	ll res = 1;
	while(n) {
		if(n&1) res = res*x%mod;
		x = x*x%mod;
		n >>= 1;
	}
	return res;
}
// 组合数阶乘预处理
void init() {
	fen[0] = 1;
	for(int i = 1; i <= N; ++i)
		fen[i] = 1ll*fen[i-1]*i%mod;

	mu[N] = pow_mod(fen[N],mod-2);
	// cout << mu[N-1] << endl;
	for(int i = N; i >= 1; --i)  {
		mu[i-1] = 1ll*mu[i]*i%mod;
		// cout << mu[i] << endl;
	}
}

int C(int a,int b) {
	if(b < 0 || a < b) return 0;
	return 1ll*fen[a]*mu[b]%mod*mu[a-b]%mod;
}

int cnt[maxn];
int main() {
	init();
	int n;
	// cout << fen[1] <<" " << mu[1] <<" " << mu[0] << endl;
	scanf("%d", &n);
	for(int i = 0; i < n; ++i) {
		int x;
		scanf("%d", &x);
		cnt[x]++;
	}
	for(int i = 1; i <= N; ++i)
		for(int j = 2*i; j <= N; j += i) {
			cnt[i] += cnt[j];
		}
	// cout << cnt[15] << endl;
	for(int i = 1; i <= 10; ++i) {
		for(int j = N; j >= 1; --j) {
			dp[j] = C(cnt[j], i);
			// cout <<i <<" " << j <<" " << dp[j] << endl;
			for(int k = j+j; k <= N; k += j) 
				dp[j] = (dp[j]-dp[k]+mod)%mod;
		}
		if(dp[1] > 0) {
			printf("%d\n", i);
			exit(0);
		}
	}
	puts("-1");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值