模拟赛 c7-D wowogcd
题目描述
有 \(N\) 根刻度条,初始长度 \((A_i)\)。
每次操作可任选一根刻度条,长度 + 1;最多执行 \(K\) 次操作。
操作完成后,设所有长度的最大公约数为 \(g\),求能得到的最大 \(g\)。
输入格式
- 第一行两个整数 \((N,K)\)
- 第二行 \(N\) 个整数 \((A_1,A_2,...,A_N)\)
输出格式
输出最大可行 \(g\)
样例输入 1
3 5
2 5 8
样例输出 1
5
样例解释
调整后全部变为 5,总增量 \((3+0+2=5 \le K),(\gcd(5,5,5)=5)\),无法得到更大公约数。
样例输入 2
4 10
6 10 15 21
样例输出 2
6
样例解释
调整后数字公约数为 \(6\),总增量 \(8 \le 10\)。
数据范围
- 部分分:\((N\le 2000,\ A_i\le 2000,\ K\le 2000)\)
- 满分:\((1\le N \le 3\times 10^5,\ 0\le K \le 10^{18},\ 1\le A_i \le 3\times 10^5)\)
算法分析
由于要最大的g,所以可以得到一个式子:射\(f(g)=Σi=1到n,((g-A_i)\ mod\ g)\ mod\ g\ ,\ ans=Max{f(1),f(2),...}\),
将式子变形一下,可以通过前缀和O(1)查询区间和,每一次枚举\(g\),所代表的区间为\([kg+1,(k+1)g]\),目标值为\(l+g-1\),这一段的代价为\(个数\times (l+g-1)-数值的和\),最后只要枚举很少的\(g\)就可以了,完美AC。
AC代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll k;
int ans = 1;
int mx = 0;
ll sum = 0;
int a[500005];
ll cnt[300005];
ll b[300005];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i = 1; i<=n; i++){
cin>>a[i];
mx = max(a[i],mx);
sum+=a[i];
}
ll c = (sum+k)/n;
if(c>mx){
cout<<c;
return 0;
}
for(int i = 1; i<=n; i++){
cnt[a[i]]++;
}
for(int i = 1; i<=mx; i++){
b[i] = b[i-1]+cnt[i]*i;
cnt[i]+=cnt[i-1];
}
for(int i = 2; i<=c; i++){
ll t = 0;
for(int j = 1; j<=mx; j+=i){
int tmp = min(mx,j+i-1);
ll t1 = cnt[tmp]-cnt[j-1];
ll t2 = b[tmp]-b[j-1];
t+=t1*(j+i-1)-t2;
if(t>k){
break;
}
}
if(t<=k){
ans = i;
}
}
cout<<ans;
return 0;
}
总结
这道题的思路很难想,也不好证明,代码也有一点难度,算数学题,个人认为可以评到青题,算一道比较难的数学证明题。

浙公网安备 33010602011771号