Codeforces Global Round 17 D. Not Quite Lee (裴蜀定理)

题目链接:https://codeforces.com/contest/1610/problem/D

贝祖定理:若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。

n个整数间的裴蜀定理

a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1,a2,a3,...,an为n个整数, d d d是它们的最大公约数,那么存在整数 x 1 , . . . , x n x_1,...,x_n x1,...,xn使得 x 1 ∗ a 1 + x 2 ∗ a 2 + . . . x n ∗ a n = d x_1*a_1+x_2*a_2+...x_n*a_n=d x1a1+x2a2+...xnan=d
特别来说,如果 a 1 , . . . , a n a_1,...,a_n a1,...,an存在任意两个数是互质的(不必满足两两互质),那么存在整数 x 1 , . . . , x n x_1,...,x_n x1,...,xn使得 x 1 ∗ a 1 + x 2 ∗ a 2 + . . . + x n ∗ a n = 1 x_1*a_1+x_2*a_2+...+x_n*a_n=1 x1a1+x2a2+...+xnan=1。证法类似两个数的情况。

借用一下某群友做好的图,下面第一点很容易想到,关键在于裴蜀定理的应用。
在这里插入图片描述
然后从题解的分析可以看到,当 g c d gcd gcd 为奇数时,上述条件必定满足,同时这种情况必定存在 y i y_i yi为奇数,而存在 y i y_i yi为奇数又表示公约数不可能为偶数,所以 g c d gcd gcd为奇数等价于存在 y i y_i yi为奇数。
g c d gcd gcd 为偶数时,每个 y i y_i yi 必然为偶数,取 l > 0 l>0 l>0 的最大值,满足 2 l 2^l 2l 整除 g c d gcd gcd,若 2 l 2^l 2l 整除 y i / 2 y_i/2 yi/2,则 g c d gcd gcd 整除 ( y i + 1 ) y i / 2 (y_i+1)y_i/2 (yi+1)yi/2 ,否则不行,可以发现这时让 ( y i + 1 ) y i / 2 (y_i+1)y_i/2 (yi+1)yi/2 2 l 2^l 2l 取模会等于 2 l − 1 2^{l-1} 2l1,所以只有不足要求的 y i y_i yi 个数为偶数时, ∑ ( y i + 1 ) y i / 2 \sum{(y_i+1)y_i/2} (yi+1)yi/2 才被 2 l 2^l 2l 整除,所以也会被 g c d gcd gcd整除。

推导过程请看:
在这里插入图片描述

所以若存在yi为奇数的情况,无论如何都满足条件。只要存在奇数的情况都合法,所以问题是只要考虑全为偶数中合法情况数目即可。

#include <bits/stdc++.h>
using namespace std;

using ll = long long;

const int N = 2e5 + 5;
const ll MOD = 1e9 + 7;

int n, a[N], cnt[30], to[N];
 
int main(void) {
//	freopen("in.txt", "r", stdin);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		int x = 0;
		while (a[i] % 2 == 0) {
			a[i] >>= 1;
			x++;
		}
		cnt[x]++;
	}
	
	to[0] = 1;
	for (int i = 1; i <= N; i++)
		to[i] = 2 * to[i - 1] % MOD;
	
	// n-cnt[0]是偶数的个数,这里是计算出存在奇数的情况数 
	ll ans = (to[n] - to[n - cnt[0]] + MOD) % MOD;
	
	// 接下来只考虑所有的yi都是偶数的情况 
	int y = n - cnt[0];
	for (int l = 1; l < 30; l++) { // 当2^l整除gcd时,取的l的最大值 
		int x = y; // 当前可以参与选择的数的个数 
		y -= cnt[l]; // y 为满足 2^l 整除 yi/2的个数 
		if (x - y < 2) continue; // x - y = cnt[l] 小于 2 说明无这种情况可选 
		
		// 当cnt[l]中参与选择时,显然有一半是good序列,另一半不是good序列
		// 因为C(cnt,0)+C(cnt, 2)+C(cnt, 4)+...是C(cnt,0)+C(cnt,1)+...+C(cnt,cnt)的一半,也就是2^cnt的一半 
		int delta = (to[x - 1] - to[y] + MOD) % MOD; // 但是还要减去cnt[l]中选择个数为0,即C(cnt,0)的可能数,不然会被重复计算 
		// 同时还有一种解释是,若cnt[l]中任一一个yi都不选,则最大的l满足2^l|gcd必定不是当前l
		ans = (ans + delta) % MOD;
	}
	
	printf("%lld\n", ans);
	return 0;
}

我觉得挺夸张的,这种推论题居然放在D题的位置,而且还有八九百个AC的,虽说来了许多1900以上的高手,但人和人的差距还是很大呀。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值