D. Winter is here(数学+计数)

题目

题意:

    给定一个数组,要求找出所有满足条件的子序列对答案的贡献。子序列的gcd大于1,贡献为gcd*序列长度。
     1 n 200000 , 1 a i 1000000 1 ≤ n ≤ 200000,1 ≤ a_i ≤ 1000000

分析:

    由于寻找的是子序列而非子区间,所以我们不能枚举左端点来做。我们注意到,由于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;
}

发布了132 篇原创文章 · 获赞 6 · 访问量 7924

猜你喜欢

转载自blog.csdn.net/weixin_44316314/article/details/104884326

相关文章