Educational Codeforces Round 20 F. Coprime Subsequences(莫比乌斯反演)

题目链接:
点击我打开题目链接
题意:
给你一个序列,问你有多少个子序列的 gcd=1

题解:
考虑一下容斥,枚举 gcd 然后容斥,先加上所有子序列的总个数 2n1 ,然后减去 gcd=2 的,然后减去 gcd=3 的,然后减去 gcd=5 的,然后加上 gcd=6 的….为什么不减去 gcd=4 呢?
因为这些的贡献统计到 gcd=2 了,减去 gcd=2 就包含了。 f(i) 为以 i gcd 的序列数,那么 f(i)=2cnt(i)1 cnt(i) 是以 i 为因子的数的个数,然后你发现这个东西就是莫比乌斯反演,直接线性筛。
然后快速统计含有某个数的作为因子的数有多少个。因为这时考虑的约数就在 [1,100000] 这里面。 O(N) 考虑每个数的约数即可。

代码:

#include 
using namespace std;
const int mod=1e9+7;
const int maxn=123456;
typedef long long ll;
int mu[maxn],prime[maxn],cnt[maxn],Pow[maxn];
int vis[maxn];
int n;
int total;
void shai()
{
    mu[1] = 1; //固定的
    for (int i = 2; i <= maxn; i++)
    {
        if (!vis[i])  //是素数
        {
            prime[++total] = i; //记录,之后要用到 
            mu[i] = -1;  //质因数分解个数为奇数
        }
        for (int j = 1; j <= total; j++)//质数或者合数都进行的
        { 
            if (i * prime[j] > maxn) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) 
            {
                mu[prime[j] * i] = 0;
                break;
            }
            mu[prime[j] * i]  = - mu[i];
            //关键,使得它只被最小的素数筛去。例如i等于6的时候。
            //当时的素数只有2,3,5。6和2结合筛去了12,就break了
            //18留下等9的时候,而9*2=18筛去
        }
    }
}
int main()
{
    ll ans = 0 ;
    int x;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        for(int j=1;j*j<=x;j++)
        {
            if(x%j==0){
                cnt[j]++;
                if(j!=x/j)
                {
                    cnt[x/j]++;     
                }
            }
        }
    }
    Pow[0]=1;
    for(int i=1;i1]*2)%mod;
    }
    shai();
    for(int i=1;i1)*mu[i])%mod;
    }
    ans=(ans+mod)%mod;
    cout<return 0;
}

你可能感兴趣的:(ACM_莫比乌斯反演,ACM_容斥原理,codeforces)