传送门:https://vjudge.net/problem/HDU-6237
思路:
因为要求完成一系列操作以后每一堆石子的个数都是一个数(大于1)的倍数,因此可以推出,这些石子数的总和也一定是这个数的倍数,所以,我们要求的这个数,一定是石子数总和的一个质因子(因为凑素因子的公因子比凑素因子的倍数为素因子更加容易),所以直接枚举和的所有质因子,然后让所有石子对于这个这个质因子取余,重新得到一个序列,我们接下来需要做的就是将得到的这个新序列凑成这个质因子的倍数的序列(因为石子数的总和是该质因子的倍数,所以这个操作一定能实现),接下来,将新序列从小到大排序,并求出它的总和为sum2,因为要求操作数最少,即要求移动的石子数最少,所以从后往前遍历,每次的操作数就是当前这个数与素因子的差值,同时sum2也应该减去这个素因子,代表有一个素因子已经凑好了。枚举每一个素因子,求最小值即可。(注意long long).
具体细节见代码。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <iostream> using namespace std; typedef long long LL; const int maxn = 1e5+200; LL num[maxn]; LL len ; LL solve(LL sum) { vector<LL> dic; dic.clear(); for(LL i = 2;i*i<=sum;i++) { if(sum%i==0) { dic.push_back(i); while(sum%i==0) sum/=i; } } if(sum>1) dic.push_back(sum); //sort(dic.begin(),dic.end()); LL ans = 1e17; for(int i = 0;i<dic.size();i++) { LL sum1 = 0,ans1 = 0; vector<LL> s; s.clear(); for(int j= 0 ;j<len;j++) { if(num[j]%dic[i]!=0) { s.push_back(num[j]%dic[i]); sum1 += num[j]%dic[i]; } } sort(s.begin(),s.end()); for(int j = s.size()-1;j>=0;j--) { ans1+=(dic[i]-s[j]); sum1-=dic[i]; if(sum1<=0) break; } ans = min(ans,ans1); } return ans ; } int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); len = n; LL sum = 0; for(int i = 0;i<n;i++) { scanf("%lld",&num[i]); sum+=num[i]; } LL ans = solve(sum); printf("%lld ",ans); } return 0; }