Codeforces839D Winter is here 容斥

/**
题目:Codeforces839D Winter is here
链接:http://codeforces.com/contest/839/problem/D
题意:给定n个数,求所有的最大公约数>1的子集的贡献之和。
一个子集的贡献为最大公约数*子集大小
思路:
cnt[i]表示约数为i的数的个数。
ans[i]表示约数为i的所有k的和。

已知ans[i]可以求得贡献为i*ans[i];

cnt[i]个数的长度为(sigma(k))cnt[i]*(2^(cnt[i]-1)); 即所有子集长度的和。
但是其中有些子集的gcd不是等于i。所以要减去。
逆序枚举,ans[i] = cnt[i]*(2^(cnt[i]-1)) - sigma(ans[j]);(j%i==0&&j/i>1)

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
#define ms(x,y) memset(x,y,sizeof x)
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 100;
int cnt[maxn];
int sum[maxn];
LL ans[maxn];
LL Pow(LL x, int y)
{
    LL p = 1;
    while (y)
    {
        if (y & 1) p = p*x%mod;
        x = x*x%mod;
        y >>= 1;
    }
    return p;
}
int main()
{
    int T, cas = 1;
    int n;
    while (scanf("%d",&n)==1)
    {
        int x, mas = 0;
        ms(cnt,0);
        ms(sum,0);
        ms(ans,0);
        for(int i = 1; i <= n; i++){
            scanf("%d",&x);
            mas = max(mas,x);
            sum[x]++;
        }
        for(int i = 2; i < maxn; i++){
            for(int j = i; j < maxn; j+=i){
                cnt[i] += sum[j];
            }
        }
        LL res = 0;
        for(int i = mas; i >= 2; i--){
            if(cnt[i]==0) continue;
            LL num = 0;
            for(int j = 2*i; j <= mas; j+=i){
                num = (num+ans[j])%mod;
            }
            ans[i] = (cnt[i]*Pow(2,cnt[i]-1)%mod-num+mod)%mod;
            res = (res+ans[i]*i%mod)%mod;
        }
        printf("%I64d
",res);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/7356919.html