题目链接:传送门
思路:
1、求出sum的所有质因子c[i] ( 1<=i<=tot )
2、判断如果当前x = c[i]时,求出当前x下所需要的最小和ans
3、最小和ans的求法:
求出1~n中a[i]对x取模后的数组b[i],对b[i]进行排序,
分别取出b的左右两个端点,如果当前b[l] + b[r] > x,则以较小的代价ans += (x - b[r])将b[r]化为x,同时削减b[l] -= (x - b[r]);
如果b[l] + b[r] <= x,增加b[r]的值,让b[r]的值尽可能变大。
(这样可以让b[l]尽可能小,让b[r]尽可能大,同时保证每次ans增加的值最小)。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n,tot;
ll b[N],a[N],c[N];
void fj(ll sum)
{
tot = 0;
ll tp = (ll)sqrt(1.0*sum);
for(ll i=2;i<=tp;i++){
if(sum%i == 0){
c[++tot] = i;
while(sum%i == 0){
sum /= i;
}
}
}
if(sum > 1LL){
c[++tot] = sum;
}
}
ll f(ll x)
{
ll ans = 0;
for(int i=1;i<=n;i++) b[i] = a[i]%x;
sort(b+1,b+1+n);
int l = 1,r = n;
while(l<r){
if(b[l] + b[r] > x){
//a[l] decrease
ans += (x - b[r]);
b[l] -= (x - b[r]);
r--;
}
else{
//a[r] increase
ans += b[l];
b[r] += b[l];
l++;
}
}
return ans;
}
int main(void)
{
int T;
cin>>T;
while(T--){
cin>>n;
ll sum = 0,mx = 0;
for(int i=1;i<=n;i++) cin>>a[i],sum += a[i],mx = max(mx,a[i]);
fj(sum);
//最坏情况,把所有物品放到mx上面
ll ans = sum - mx;
for(int i=1;i<=tot;i++){
ans = min(ans,f(c[i]));
}
cout<<ans<<endl;
}
return 0;
}