Codeforces Round #519 by Botan Investments F. Make It One (莫比乌斯反演)

版权声明:转载注明下出处就行了。 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
*/



猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/83782191