传送门:Codeforces 839D
题意:给出一个序列,求取出一个字序列,当他们的GCD大于1时,将贡献子序列所有数的gcd * 子序列的长度,问总贡献是多少。
思路:感觉很像前几次的多校一个题,但是想了很久也没想出来该怎么求∑i∗C(n, i)(i >= 1)
然而,其实∑i∗C(n, i) == n * ∑ C(n - 1, i - 1)(i >= 0),化成组合数的定义式立马就能看出来。。
这样∑i∗C(n, i) == n * 2 ^ (n - 1). 然后我们枚举gcd的大小,能产生i当gcd的序列中必定都是i的倍数,因此我们只需要统计i的倍数有多少个,然后利用上面的式子计算,很容易发现有重复计算的部分,倒着容斥一遍就行了,最后再乘以个i加到答案里即可。
膜拜大佬17行ac代码:点击打开链接 题解也很言简意赅,不过稍微有点瑕疵:ans[i] = f[i] - f[2i] - f[3i]...
还有莫比乌斯的题解思路:点击打开链接
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1000010;
const int mod = 1e9 + 7;
int cnt[MAXN], u[MAXN], f[MAXN];
int main()
{
int n, t, up = 0;
cin >> n;
for(int i = 0; i < n; i++)
{
scanf("%d", &t);
cnt[t]++;
up = max(up, t);
}
u[0] = 1;
for(int i = 1; i <= up; i++)
u[i] = (2 * u[i - 1]) % mod;
ll ans = 0;
for(int i = up; i >= 2; i--)//逆序求解,因为可以顺便进行容斥
{
int tmp = i, k = 0;
while(tmp <= up) k += cnt[tmp], tmp += i;
if(k == 0) continue;
f[i] = (1ll * k * u[k - 1]) % mod;
for(int j = i + i; j <= up; j += i)
f[i] = (f[i] - f[j] + mod) % mod;
ans = (ans + 1ll * i * f[i]) % mod;
}
cout << ans << endl;
}