ARC126C Maximize GCD

该文章介绍了ARC126C问题的解题思路,涉及一个正整数序列,允许进行有限次操作,每次将某个元素加1,目标是最大化序列中所有数的最大公约数。当操作次数足够多时,可以先将所有元素提升至最大值,然后逐步增加,而当操作次数有限时,需要通过枚举和区间求和优化方法找到最大可能的最大公约数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ARC126C Maximize GCD

题目大意

给一个长度为 n n n的正整数序列 A = { a 1 , a 2 , … a n } A=\{a_1,a_2,\dots a_n\} A={a1,a2,an},你可以做如下的操作,最多做 k k k次,最少做 0 0 0次。

  • 选择 i ∈ { 1 , 2 , … n } i\in\{1,2,\dots n\} i{1,2,n},将 a i a_i ai 1 1 1

gcd ⁡ ( a 1 , a 2 , … a n ) \gcd(a_1,a_2,\dots a_n) gcd(a1,a2,an)的最大的可能值。

2 ≤ n ≤ 3 × 1 0 5 , ≤ k ≤ 1 0 18 , 1 ≤ a i ≤ 3 × 1 0 5 2\leq n\leq 3\times 10^5,\leq k\leq 10^{18},1\leq a_i\leq 3\times 10^5 2n3×105,k1018,1ai3×105


题解

令所有 a i a_i ai的和为 s u m sum sum,最大的 a i a_i ai值为 m x mx mx

显然,若 k ≥ n × x − s u m k\geq n\times x-sum kn×xsum,那么我们可以先把每个 a i a_i ai的值加到 m x mx mx,然后不断进行操作,每次操作将每个 a i a_i ai都加 1 1 1,一直操作到不能操作为止。此时的答案为 m x + ⌊ k − ( n × x − s u m ) n ⌋ mx+\lfloor\dfrac{k-(n\times x-sum)}{n}\rfloor mx+nk(n×xsum)

如果 k < n × x − s u m k<n\times x-sum k<n×xsum,那么无论加多少次 1 1 1 a i a_i ai的最小值一定不会超过 m x mx mx,所以 gcd ⁡ ( a 1 , a 2 , … a n ) \gcd(a_1,a_2,\dots a_n) gcd(a1,a2,an)也不会超过 m x mx mx,我们考虑枚举答案。

假设当前枚举的 gcd ⁡ \gcd gcd d d d,则我们需要将每个 a i a_i ai增至 a i a_i ai d d d的倍数。

因为 a i a_i ai最大只有 3 × 1 0 5 3\times 10^5 3×105,所以我们可以用桶来存 a i a_i ai

枚举 d d d的倍数 j j j,则所有满足在 [ j − d + 1 , j ] [j-d+1,j] [jd+1,j]这一块中的 a i a_i ai都应该加到 j j j。对于这样的每一块,我们只需要求出将这一块中所有 a i a_i ai加到 j j j的操作次数,最后将每一块的操作次数求和。如果和小于等于 k k k,则可以使最大公约数为 d d d,否则不行。

对于区间元素数量和区间和,用前缀和来维护即可。

最坏的情况下,需要枚举次数为 m x + m x 2 + ⋯ + 1 = ∑ i = 1 m x m x i ≈ m x ln ⁡ m x mx+\dfrac{mx}{2}+\cdots+1=\sum\limits_{i=1}^{mx}\dfrac{mx}{i}\approx mx\ln mx mx+2mx++1=i=1mximxmxlnmx,所以时间复杂度为 O ( m x ln ⁡ m x ) O(mx\ln mx) O(mxlnmx)

code

#include<bits/stdc++.h>
using namespace std;
int n;
long long k,sum=0,mx=0,v,a[300005],z[300005],s[300005];
int main()
{
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		sum+=a[i];mx=max(mx,a[i]);
	}
	if(k>=mx*n-sum){
		k-=mx*n-sum;
		mx+=k/n;
		printf("%lld",mx);
		return 0;
	}
	for(int i=1;i<=n;i++) ++z[a[i]];
	for(int i=1;i<=mx;i++){
		s[i]=s[i-1]+z[i]*i;
		z[i]+=z[i-1];
	}
	for(int i=mx;i>=1;i--){
		v=0;
		for(int j=i;j<=mx;j+=i){
			v+=1ll*(z[j]-z[j-i])*j-(s[j]-s[j-i]);
			if(v>k) break;
		}
		v+=1ll*(z[mx]-z[mx/i*i])*(mx/i*i+i)-(s[mx]-s[mx/i*i]);
		if(v<=k){
			printf("%d",i);
			break;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值