版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/83627570
题意
给出长度为 的正整数序列,要求找出一个最小的子集,使得子集中的数的gcd等于1。没有的话输出-1,否则输出子集大小。序列中的数 。
题解
暴力的做法就是从1开始枚举子集的大小,不同的子集个数 ,肯定要超时。我们可以设 表示集合大小为 且集合 等于 的集合个数。当 时,说明存在集合大小为 的且其 。
这里可以采取容斥来解决,
序列中能被
整除的数的个数。
:在序列中能被
整除的数中选
个。
为什么这么减呢?因为 可能包括了能被 整除的集合。
这里 的大小最大不超过7,因为要保证所有数互质,因为6个数不互质,7个数一定互质,那么每个数必须包含6个质因子。举个例子来说, 10 ,6 ,15 其质因子组成是 (2,5), (2,3), (3,5),每当gcd两个,都会消去不同质因子,比如 ,所以如果7个数要够消的话,那么一个数要有6个质因子,每和一个数gcd就消去一个。
构造一下7个数的情况。
#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;
}