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

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/83627570

题目链接

题意

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

题解

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

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

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

这里 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 ,所以如果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]

#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;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/83627570