题意:有n堆物品,问你如果要把每堆物品都分成若干集合,且所有集合大小差距不超过1,最少需要分成多少堆
题解:因为1<a[i]<1e9,所以我们可以分块,枚举集合的大小i[1,sqrt(mx)],我们对每个集合判断能否把这个集合分成若干集合,其中每个集合的大小为i或i+1,如果全都可以,分成尽量少的集合,然后我们枚举把第一堆物品分成的集合的个数i[1,a[1]],所以我们得到一个sz a[1]/i,然后对后面的集合判断能否分成若干个集合,其中每个集合的大小为i或i-1(如果a[1]%i==0,那么i-1,i也需要判断),然后取上面所有可行方案的最小答案就行时间复杂度n*sqrt(1e9)
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=5e2+10;
#define ll long long
ll ans, mx;
int n;
ll a[N];
ll check(ll x,ll y) {
ll a = x / (y + 1);
ll b = x % (y + 1);
if(b == 0)return a;
b = y - b;
if(b < 0) return 0;
if(b > a) return 0;
return a + 1;
}
void solve1() {//divede into size x
mx = sqrt(mx);
for (ll i = 1; i <= mx; i++) {
bool flag = 1;
ll tmp=0;
for (int j = 1; j <= n; j++) {
ll x = check(a[j],i);
if(x) tmp += x;
else{flag = 0; break;}
}
if(flag)
ans = min(ans,tmp);
}
}
void solve2() {
for (ll k = 1; k <= mx; k++){
bool flag = 1;
bool dir = 0;
ll sz = a[1] / k;
if(a[1] % k == 0) dir=1;
if(dir){
for(int i = 2; i <= n; i++){
ll x = check(a[i],sz-1);
ll y = check(a[i],sz);
if (x) {
if (y);
else {
dir=0;
sz--;
break;
}
}
else {
if (y) {
dir=0;
break;
}
else {
flag = 0;
break;
}
}
}
}
ll tmp=0;
for (int j = 1; j <= n; j++) {
ll x = check(a[j],sz);
if(x) tmp += x;
else{flag = 0; break;}
}
if(flag)
ans = min(ans,tmp);
}
}
int main()
{
//freopen("in.in","r",stdin);
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%lld",&a[i]),ans += a[i],mx = max(mx,a[i]);
solve1();
solve2();
printf("%lld\n",ans);
}