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

  

原文地址:https://www.cnblogs.com/RUSH-D-CAT/p/6841073.html