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

题目

题意:

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

分析:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值