版权声明:欢迎大家转载,转载请注明出处 https://blog.csdn.net/hao_zong_yin/article/details/83508578
题意:有n(1e5)堆石子,第i堆石子有a[i](1e5)个,现在可以进行任意次操作,每次操作可以把一个石头从一堆挪到另一堆,问最少操作几次可以达成这个条件:存在一个x,使得每堆的石子数量都是x的倍数
思路:首先对石子总数进行质因分解,x一定是这些素因子中的一个,这个是我凭感觉蒙的,事实证明确实是对的。然后枚举素因子x,算每个素因子对应的最小移动次数,我们设b[i]=a[i]%x,那么我们对b从小到达排序,可以这么理解,为了实现移动次数最少,我们应该把值小的那些b移走,补充值大的那些b,进一步我们应该将值最小的b补充给值最大的b,这个记录一个l,r,模拟一下就可以了,要注意的是因为x是素因子,所以移动一定是合法的,这样每个素因子x对应的最小移动次数我们就算完了,最后在这些值中取一个最小值就可以
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll INF = 1e14;
int T, N;
ll a[maxn], p[maxn], b[maxn];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &N);
ll sum = 0;
for (int i = 1; i <= N; i++) scanf("%lld", &a[i]), sum += a[i];
int cnt = 0;
for (ll i = 2; i * i <= sum; i++) {
if (sum % i == 0) {
p[++cnt] = i;
while (sum % i == 0) sum /= i;
}
}
if (sum > 1) p[++cnt] = sum;
ll ans = INF;
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= N; j++) {
b[j] = a[j] % p[i];
}
sort(b+1, b+1+N);
int l = 1, r = N;
ll res = 0;
while (l < r) {
if (b[l] == 0) { l++; continue; }
res += b[l];
while (b[l] != 0) {
if (b[r] + b[l] < p[i]) {
b[r] += b[l];
b[l] = 0;
}
else {
b[l] -= p[i] - b[r];
b[r--] = p[i];
}
}
l++;
}
ans = min(ans, res);
}
printf("%lld\n", ans);
}
return 0;
}