版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/83782191
题目链接:F
参考博客:https://blog.csdn.net/qq_32506797/article/details/83507061
可以再看看用组合数学+dp写的,对比下发现,差不多https://blog.csdn.net/LJD201724114126/article/details/83780844
题意:给n个数(n<=3e5),每个数<=3e5,选择最少的数,使得他们的gcd为1。输出这个最小子集的大小。
题解:我们先看下莫比乌斯反演的两条式子:
其实莫比乌斯有两种描述:
莫比乌斯第一种描述,一般是这种:
莫比乌斯第二种描述,这种也可以而且有些题这种更好:
这题我们用第二条式子,设f(len,n)为集合大小为len(len<=7),gcd为n的方案数,那么F(len,n)表示为集合大小为len,gcd为n的倍数的方案数。即我们验证 f(len,1) (集合大小为len,gcd为1的方案数)不为0就可以求得解了,至于集合大小len为什么是不超过7的,看这篇博客,有解析https://blog.csdn.net/LJD201724114126/article/details/83780844
第一篇是大佬写的,我写的,有点奇怪,在codeblocks输出正确结果,但提交上去,输出的又是另外中结果,
还是贴大佬的代码好了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5+10;
const int MOD = 1e9+7;
bool isn[N];
int mu[N], p[N];
LL inv[N], fac[N];
void init() {
inv[1] = 1;
for(int i = 2; i < N; ++i) inv[i] = MOD-(MOD/i*inv[MOD%i]%MOD);
fac[0] = 1, inv[0] = 1;
for(int i = 1; i < N; i++){
fac[i] = fac[i-1]*i%MOD;
inv[i] = inv[i-1]*inv[i]%MOD;
}
// printf("%lld %lld \n",inv[1],inv[2]);
int pnt = 0;
isn[1] = 1; mu[1] = 1;
for(int i = 2; i < N; i++) {
if(!isn[i]) { p[pnt++] = i; mu[i] = -1; }
for(int j = 0; j<pnt && i*p[j]<N; j++) {
isn[i*p[j]] = 1;
if(i%p[j] == 0) { mu[i*p[j]] = 0; break; }
mu[i*p[j]] = -mu[i];
}
}
}
inline LL C(int a, int b) { return fac[a]*inv[b]%MOD*inv[a-b]%MOD; }
int cnt[N];
int main() {
init();
int n, g; scanf("%d", &n);
for(int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
if(i == 1) g = x;
else g = __gcd(g, x);
cnt[x]++;///存储值为x的,有多少个?
}
if(g != 1) { puts("-1"); exit(0); }
for(int i = 1; i < N; i++)
for(int j = i+i; j < N; j += i)
cnt[i] += cnt[j]; ///求序列中能被i整除的个数
for(int i = 1; i <= min(n, 7); i++) {///集合大小为i
LL sum = 0;
for(int j = 1; j < N; j++) if(cnt[j] >= i) {///gcd为1的倍数j
(sum += mu[j]*C(cnt[j], i)) %= MOD;
// printf("i=%d,mu[%d]=%d sum=%lld\n",i,j,mu[j],sum);
}
///说明存在集合大小为i,gcd为1的方案,直接输出
if(sum) { printf("%d\n", i); break; }
}
return 0;
}
这篇是我的:
///注意long long 和int的转化
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
const int maxn=3e5+10;
const int N=3e5;
LL jie[maxn],ni[maxn];
int mu[N], vis[N], prime[N];
int tot;///用来记录prime的个数
LL pow(LL x,LL y)
{
LL sum=1;
while(y)
{
if(y&1) sum=sum*x%mod;
x=x*x%mod;
y>>=1;
}
return sum;
}
void init()
{
memset(vis,0,sizeof(vis));
mu[1] = 1;
for(int i = 2; i < maxn; i ++){
if(!vis[i]){
prime[tot ++] = i;
mu[i] = -1;
}
for(int j = 0; j < tot && i * prime[j] < maxn; j ++){
vis[i * prime[j]] = 1;
if(i % prime[j]) mu[i * prime[j]] = -mu[i];
else{
mu[i * prime[j]] = 0;
break;
}
}
}
jie[0]=jie[1]=1;
for(int i=2;i<=N;i++)
jie[i]=1LL*jie[i-1]*i%mod; ///前i项的阶乘
ni[N]=pow(jie[N],mod-2)%mod; ///逆元
for(int i=N;i>=1;i--) ///线性筛逆元
{
ni[i-1]=1LL*ni[i]*i%mod;
}
// printf("%lld %lld",ni[1],ni[2]);
}
LL C(LL a,LL b)
{
if(b<0||a<b) return 0;
return 1LL*jie[a]*ni[b]%mod*ni[a-b]%mod;
}
int cnt[maxn];
int main()
{
init();
int n;
scanf("%d",&n);
memset(cnt,0,sizeof(cnt));
int x;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
cnt[x]++; ///存储值为x的,有多少个?
}
for(int i=1;i<=N;i++) ///求序列中能被i整除的个数
for(int j=2*i;j<=N;j+=i)
cnt[i]+=cnt[j];
bool flag=true;
for(int i=1;i<=min(n,7);i++) ///集合大小为i
{
LL sum=0;
for(int j=1;j<=N;j++){ ///gcd为1的倍数j
if(cnt[j]>=i){
sum=(sum+1LL*mu[j]*C(cnt[j],i))%mod;
}
}
if(sum) { ///说明存在集合大小为i,gcd为1的方案,直接输出
printf("%d\n",i);flag=false;
break;
}
}
if(flag) puts("-1"); ///不存在,输出-1
return 0;
}
/*
i=1,mu[1]=1 sum=3
i=1,mu[2]=-1 sum=1
i=1,mu[3]=-1 sum=-1
i=1,mu[5]=-1 sum=-3
i=1,mu[6]=1 sum=-2
i=1,mu[10]=1 sum=-1
i=1,mu[15]=1 sum=0
i=2,mu[1]=1 sum=3
i=2,mu[2]=-1 sum=2
i=2,mu[3]=-1 sum=1
i=2,mu[5]=-1 sum=0
i=3,mu[1]=1 sum=1
3
*/