codeforces 839d Winter is here

一场给dalao们吐槽到不行。全场B题最后终测挂了9/10的div2。
hack频频出错。B题比D题还难的场。。
多校中插一脚cf。hhhhhhh

题目传送门

题意:
给一个序列,要求找一个可以不连续长度为k的子序列满足下标 i1<i2<i3<...<ik
有满足 p=gcd(ai1,ai2..aik),p>1 可以贡献p * k的价值。
现在问对于这个序列的所有情况的贡献是多少。

思路:
其实可以不要被这个下标蒙蔽了。下标没什么的。只是限定你只能使用一次这个序列而已。
比赛的时候发现其实对于每一个位置答案就是 iiCin ,这里的n为这个数与这个数的倍数的总和。
然后答案可能有重复容斥一下就好了(本人容斥比较弱。就写过两次。估计如果比赛真要写容斥,脑子抽的话容斥怎么写都不对)
然后发现 iCin 这个东西怎么那么难算啊。算不出啊。是不是思路错了。。然后就挂机一个钟了
其实dalao都说。不难发现。 Cin=Ci1n1n/i
所以 iCin=nCi1n1
然后 Ci1n12n1
所以最后转换成 n2n1 即可

代码如下:

/*
@resources: codeforces 839d
@date: 2017-08-13
@author: QuanQqqqq
@algorithm: 容斥 
*/
#include 

#define ll long long
#define MAXN 2000005
#define MOD 1000000007

using namespace std;

ll tot[MAXN], mark[MAXN];
ll c[MAXN];

ll ksm(ll a, ll n) {
    ll ans = 1, t = a;
    while (n) {
        if (n & 1) {
            ans = (t * ans) % MOD;
        }
        t = (t * t) % MOD;
        n >>= 1;
    }   
    return ans;
}

int main() {
    ll n, maxt = 0, tmp;
    scanf("%lld", &n);
    for (ll i = 0; i < n; i++) {
        scanf("%lld", &tmp);
        tot[tmp]++;
        maxt = max(maxt, tmp);
    }
    for (ll i = 2; i <= maxt; i++) {
        ll sum = 0;
        for (ll j = i; j <= maxt; j += i) {
            sum += tot[j];
        }
        if (sum == 0) {
            continue;
        }
        if (mark[sum] != 0) {
            c[i] = mark[sum];
            continue;
        }
        c[i] = (sum * ksm(2, sum - 1)) % MOD;
        mark[sum] = c[i];
    }
    ll ans = 0;
    for (ll i = maxt; i >= 2; i--) {
        for (ll j = i * 2; j <= maxt; j += i) {
            c[i] -= c[j];
            c[i] = (c[i] % MOD + MOD) % MOD;
        }
        ans += (c[i] * i) % MOD;
        ans %= MOD;
    }
    printf("%lld\n", ans);
}

你可能感兴趣的:(容斥)