zoukankan      html  css  js  c++  java
  • Codeforces 803F Coprime Subsequences (容斥)

    Link: http://codeforces.com/contest/803/problem/F

    题意:给n个数字,求有多少个GCD为1的子序列。

    题解:容斥!比赛时能写出来真是炒鸡开森啊!

    num[i]: 有多少个数字是 i 的倍数。

    所有元素都是1的倍数的序列有:$2^n-1$个。先把$2^n-1$设为答案

    所有元素都是质数的倍数的序列有:$sum 2^{num[p_1]} - 1$个,这些序列不存在的,得从答案中减去。

    所有元素都是两质数之积的倍数的序列有:$sum 2^{num[p_1*p_2]} - 1$个,这些序列两次扫黄都在现

    场,我们应减一次,但实际减了两次,多减了一次,所以要加回到答案中。

    然后考虑,所有元素都是3,4,5......个质数之积的倍数的序列。

    依次类推。于是就可以容斥了。

    PS: 要先预处理好一个数字,能被拆成几个素数之积。而且同一个素数不能出现两次或以上。

    【不优雅の】code: 

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    using namespace std;
    typedef long long ll;
    const int MAXN = 100000+10;
    const int MAXM = 100000+10;
    ll prime[MAXN+1];
    ll ct[MAXN], sgn[MAXN];
    ll n, a[MAXN], bad[MAXN];
    void getPrim()
    {
        memset(sgn, 1, sizeof(sgn));
        memset(prime, 0, sizeof(prime));
        for(int i=2;i<=MAXN;i++)
        {
            if(!prime[i]){
               prime[++prime[0]] = i;
            }
            for(int j=1;(j<=prime[0])&&(prime[j]<=(MAXN/i));j++)
            {
                prime[prime[j]*i] = 1;
                if(i%prime[j]==0) break;
            }
        }
    }
    
    void getFactor(ll x)
    {
        ll cnt = 0, i;
        ll tmp = x;
        for(i = 1; prime[i] * prime[i] <=tmp ;i++)
        {
            if(tmp % prime[i] == 0)
            {
                int c = 0;
                while(tmp % prime[i] == 0)
                {
                    c ++;
                    tmp /= prime[i];
                }
                if(c >= 2)
                {
                    bad[x] = 1;
                    return;
                }
                cnt ++;
            }
        }
        if(tmp!=1)
        {
            cnt ++;
        }
        ct[x] = cnt;
    }
    
    const ll MOD = 1000000007;
    ll mpow(ll a, ll n)
    {
        ll ret = 1;
        while(n)
        {
            if(n & 1)
            {
                ret = (ret * a);
                ret %= MOD;
            }
            a = a * a % MOD;
            n >>= 1;
        }
        return ret;
    }
    
    ll num[MAXN];
    int main()
    {
        getPrim();
        for(int i=1;i<MAXN;i++)
        {
            getFactor(i);
        }
        scanf("%lld", &n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld", &a[i]);
            for(ll j=1;j*j<=a[i];j++)
            {
                if(a[i]%j==0)
                {
                    if(j*j!=a[i]) num[a[i]/j] ++;
                    num[j] ++;
                }
            }
        }
        ll ans = 0;
        for(int i=2;i<MAXN;i++)
        {
            if(num[i]>0 && ct[i]>0 && !bad[i])
            {
                //cout << i << " " << num[i] << " " << ct[i] << endl;
                ans += (ll)( (ct[i]%2==1)?(1):(-1) ) * (mpow(2, num[i])-1); 
                ans %= MOD;
            }
        }
        ans = (mpow(2, n) - ans + MOD) % MOD;
        cout << (ans-1+MOD)%MOD << endl;
    }
    

    官方题解提到了莫比乌斯函数,最终答案的表示为$sumlimits_{i=1}^{1e5} µ(i)(2^{num[i]}-1)$

    套了下KuangBin巨巨的模板。重写了遍。

    #include <iostream>
    #include <cstring>
    using namespace std;
    typedef long long LL;
    const int NICO = 100000+2;
    const int MOD = 1000000000 + 7;
    LL n, a[NICO], cnt[NICO], po[NICO], mo[NICO];
    bool chk[NICO];int prime[NICO];
    void init()
    {
        po[0] = 1, mo[1] = 1;
        for(int i=1;i<NICO;i++) po[i] = 2*po[i-1]%MOD;
        memset(chk, 0, sizeof(chk));
        int tot = 0;
        for(int i=2;i<NICO;i++)
        {
            if(!chk[i])
            {
                prime[tot++] = i;
                mo[i] = -1;
            }
            for(int j=0;j<tot;j++)
            {
                if(i*prime[j]>=NICO) break;
                chk[i*prime[j]] = 1;
                if(i%prime[j] == 0)
                {
                    mo[i*prime[j]] = 0;
                    break;
                } else {
                    mo[i*prime[j]] = -mo[i];
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j*j<=a[i];j++)
            {
                if(a[i]%j) continue;
                if(j*j != a[i]) cnt[a[i]/j] ++;
                cnt[j] ++;
            }
        }
    }
    
    int main()
    {
        scanf("%lld", &n);
        for(int i=1;i<=n;i++) scanf("%lld", &a[i]);
        LL ans = 0; init();
        for(int i=1;i<NICO;i++)
        {
            ans = (ans + mo[i] * (po[cnt[i]]-1) )% MOD;
        }
        cout << (ans+1000LL*MOD)%MOD << endl;
    }
    

      

  • 相关阅读:
    第二节:Android APP自动化测试之环境配置以及脚本设计(课工场)
    第一节:Appium介绍(课工场)
    Dockerfile 相关命令
    Nginx 原理及与 Apache 对比
    Windows 环境配置 Nginx
    Linux 命令大全(十九)——性能优化及常见问题
    Linux 命令大全(十八)——压缩打包
    Linux 命令大全(十七)——VI/VIM编辑器
    Linux 命令大全(十六)——计划任务
    Linux 命令大全(十五)——配置文件
  • 原文地址:https://www.cnblogs.com/RUSH-D-CAT/p/6841073.html
Copyright © 2011-2022 走看看