题目
题意:
给定一个数组,要求找出所有满足条件的子序列对答案的贡献。子序列的gcd大于1,贡献为gcd*序列长度。
分析:
由于寻找的是子序列而非子区间,所以我们不能枚举左端点来做。我们注意到,由于gcd的值并不会多,且对于每个gcd来说,它只会由它的倍数产生。所以我们考虑枚举gcd的值,对于一个值来说,产生它的就是它的倍数了。计算出它的倍数后,取出任意多的数。
那么贡献为1C[n][1]+2C[n][2]+…nC[n][n] = n2^(n-1),最后再乘一个gcd的值。但是我们考虑到并非任取出来的元素的gcd一定是它,可能是它的倍数,所以我们要去掉这些情况。那么我们从大到小枚举,维护一个f[i]表示gcd为i时的贡献(没有乘上i),这样在计算f[i]时再减去它的倍数的f值即可。
#include <iostream>
using namespace std;
typedef long long ll;
ll mod = 1e9 + 7;
const int N = 1e6;
ll cnt[N+5],f[N+5],d[N+5];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
d[0] = 1;
for (int i = 1; i <= n; i++)
{
d[i] = d[i-1] * 2 % mod;
}
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
cnt[x] ++;
}
ll ans = 0;
for (int i = N; i > 1; i--)
{
ll x = 0;
for (int j = i; j <= N; j += i )
{
x += cnt[j];
}
if( x )
{
f[i] = x * d[x-1] % mod;
for (int j = i + i; j <= N; j += i )
{
f[i] = ( f[i] - f[j] + mod ) % mod;
}
ans += (ll)i * f[i];
ans %= mod;
}
}
cout << ans << '\n';
return 0;
}