Codeforces Global Round 17 D. Not Quite Lee(gcd)

98 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。 传送门

题意:

给你一个序列a,对于数aia_i,将其写成任意aia_i个连续的数,记第aia_i组数的和为sumisum_i,现要你找出所有的sumisum_i的组合,使每个组合中的sum的和为0。

思路:

经过推理,不难发现: 对于aia_i为偶数的情况,它的贡献为ai/2+aika_i/2+a_i*k,k为任意整数,下同; 对于aia_i为奇数的情况,它的贡献为aika_i*k。 同时,我们也可以得出第一个重要结论:当选取的aia_i全为奇数时,一定合法。 继续推理可得出下一个结论:**当选取的aia_i至少有一个奇数时,一定合法。*,证明如下: 记我们选取的数的集合为b1,b2,b3......bmb_1,b_2,b_3......b_m,且其中存在x个奇数,要证明选取的数合法, 即证明:(bx+1+bx+2+......+bm)/2(b_{x+1}+b_{x+2}+......+b_{m})/2=b1k1+b2k2+......bmkmb_1*k_1+b_2*k_2+......b_m*k_m这个式子成立 而等式右边的式子又可以等价于:gcd(b1,b2......,bmb_1,b_2......,b_m)*k,这一步不多做阐述, 即要证明:(bx+1+bx+2+......+bm)/2(b_{x+1}+b_{x+2}+......+b_{m})/2=gcd(b1,b2......,bmb_1,b_2......,b_m)*k, 记S = (bx+1+bx+2+......+bm)(b_{x+1}+b_{x+2}+......+b_{m}) 即证明:gcd(b1,b2......,bmb_1,b_2......,b_m) | S/2 易得:gcd(b1,b2......,bmb_1,b_2......,b_m) | S且由于b1bmb_1到b_m中存在奇数,因而gcd(b1,b2......,bmb_1,b_2......,b_m) 一定不能整除2,所以gcd(b1,b2......,bmb_1,b_2......,b_m) | S/2一定成立, 证毕。 接下来看全为偶数的情况,先说结论:我们将所有的偶数写为2kc2^k*c的形式,则选取的序列满足其中k值最小的数为偶数时,就合法。 证明如下: 同样是上面的那个式子:gcd(b1,b2......,bmb_1,b_2......,b_m) | S/2,现在b序列全为偶数,将其中每个数都写为2kc2^k*c的形式,设最小的k为kmink_{min},把gcd(b1,b2......,bmb_1,b_2......,b_m)也写为上述形式,则其等于2kminx2^{k_{min}}*x,且x一定为奇数,不难得出x | S/2,现在只需要证明:2kmin2^{k_{min}} | S/2,我们将S/2拆成序列的形式来看就是:{b1/2+b2/2+...+bm2b_1/2+b_2/2+...+b_m2},在这个除以2之后的序列中,原来刚好包含2kmin2^{k_{min}}的数都变为了包含2kmin12^{k_{min-1}}的数,而如果这些的个数为奇数,那么再让其除以2kmin2^{k_{min}}则会出现小数,而如果个数为偶数,那么就可以让每两个2kmin12^{k_{min-1}}合并为一个2kmin2^{k_{min}},即:选取的序列满足其中k值最小的数为偶数时,就合法。 证毕。 最后的答案就用上面的结论即可得出。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9+7;
ll qpow(ll a, ll b)
{
	ll res = 1;
	while(b)
	{
		if(b&1)res = res*a%mod;
		b = b>>1;
		a = a*a%mod;
	}
	return res;
}
int sum[40];
ll a[200010];
ll suf[40]; 
int main()
{
	int n;
	cin>>n;
	ll even = 0,odd = 0;
	for(int i = 1; i <= n; i++)
	{
		cin>>a[i];
		if(a[i]%2)odd++;
		else even++;
		if(a[i]%2==0)
		{
			int k = 0;
			ll now = a[i];
			while(now%2==0)
			{
				now/=2;
				k++;
			}
			sum[k]++;
		}
	}
	for(int i = 31; i >= 1; i--)
	{
		suf[i] = suf[i+1]+sum[i];
	}
	ll ans = qpow(2,n)+mod-qpow(2,even);
	ans%=mod;
	for(int i = 1; i <= 31; i++)
	{
		if(sum[i])
		{
			ans += (qpow(2,sum[i]-1)-1)*qpow(2,suf[i+1]);
			ans%=mod;
		}
	}
	cout<<ans%mod<<endl;
}