codeforces.com/gym/105930/…
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
#define int long long
vector<int>dis(int x){
vector<int>b;
for(int i=1;i*i<=x;i++){
if(x%i==0){
b.push_back(i);
if(i*i!=x)b.push_back(x/i);
}
}return b;
}
void so(){
}signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
int n,k;cin>>n>>k;
vector<int>a(n+1);
int ma=0;
int suma=0;
for(int i=1;i<=n;i++){
cin>>a[i];
suma+=a[i];
ma=max(ma,a[i]);
}vector<int>fre(ma+1,0);
vector<int>sum(ma+1,0),pc(ma+1,0);
for(int i=1;i<=n;i++)fre[a[i]]++;
for(int i=1;i<=ma;i++){
pc[i]=pc[i-1]+fre[i];
sum[i]=sum[i-1]+fre[i]*i;
}// 目标:找最大的d,使得把所有元素变成d的倍数,所需的最少操作数 ≤ k
// 所有可能的d必须是 (suma + k) 的约数!
// 原理:操作后总和 = suma + k,而所有元素都是d的倍数 → 总和是d的倍数 → d | (suma + k)
int ta=suma+k;
auto v=dis(ta);
sort(v.begin(),v.end(),greater<int>());
for(auto x:v){
int res=0;
if(x>ma){
res+=n*x-suma;//所有都变成x
}//d<=ma
//把所有数变成>=自己的d得倍数
//每个数 a_i 必须变成 ≥ a_i 的最小 x 的倍数
//比如
//x=5
//a_i=2 → 变成 5(加 3)
//a_i=8 → 变成 10(加 2)
//a_i=9 → 变成 10(加 1)
else{
for(int t=1;(t-1)*x<=ma;t++){
int l=(t-1)*x+1;
int r=min(ma,t*x);
res+=t*(pc[r]-pc[l-1])*x-(sum[r]-sum[l-1]);
if(res>k)break;
}
}//最少操作次<=k,且剩余操作次数(k-res)是x的倍数
if((k-res)%x==0&&k-res>=0){cout<<x<<'\n';break;}
}
}
}
思路
1.把每个数映射到桶,到前缀和 2.公式变形,枚举因数