大意
有(N)种颜色的球,第(i)种球有(Ai)个,要求把球分成几个集合,使得:
- 一个集合里的球只能有一种颜色。
- 任意两个集合的球的数量相差不能超过1。
求这些球至少需要分几个集合。
思路
我们设这些集合的大小为(Ans)与(Ans+1),考虑如何判断一个(Ans)是否可行。
由于一个集合里只能有一种颜色,所以我们可以对于每一种颜色都单独考虑。
设当前颜色为(i),我们设 (Ai=Xcdot Ans+ Y),其中(Ans,Y)满足(Ans>Y)。
那么易发现,若(Y>X),则剩下的(Y)个数就算平均分到(X)个集合里,也会剩下((Y-X))个数,那么我们的(Ans)就不符合条件了。
又由于在(Ai=Xcdot Ans+ Y)式子中的(X=left lfloor Ai/Ans
ight
floor,Y=Ai\%Ans)
化简一下,一个(Ans)可行的条件为:对于任意(i),都满足(left lfloor Ai/Ans
ight
floorge Ai\%Ans)
尽管已经知道了如何判断一个(Ans)是否可行,但如果我们暴力去枚举(Ans),复杂度也是(O(N imes A)),考虑优化。
首先有一个很明显的性质就是(Ansle Min(Ai)),即(Ans)至多是(A)中的最小值。
对于(Ans)的判断式:(left lfloor Ai/Ans
ight
floorge Ai\%Ans)。
发现在(Anslesqrt{Ai})时,肯定是恒满足的,因为
(left lfloor Ai/Ans
ight
floorge sqrt{Ai}ge Ansge Ai\%Ans)
所以所有满足(Anslesqrt{Ai})的(Ans)肯定都是可行的(Ans)。
考虑(Ans>sqrt{Ai})的情况:
在这种情况下,我们的判断式就会满足(left lfloor Ai/Ans
ight
floor<sqrt{Ai}<Ans),
所以我们不妨枚举(left lfloor Ai/Ans
ight
floor)即(X)的取值,这样的复杂度是(O(Ncdotsqrt{A})),可以接受。
然后又由于要让(Ans)可行,所以我们要让(left lfloor Ai/Ans
ight
floorge Ai\%Ans),即(Ans>Xge Y).
考虑满足上述条件下的(Ans)的取值为多少:
- 由于(Ai=Xcdot Ans+ Y),(Ans=left lfloor frac{Ai-Y}{X}
ight
floor),在这种情况下,我们只知道(X)和(Ai),所以考虑(Y)的取值情况。
由于(0le Yle X),则有(frac{Ai-X}{X}le frac{Ai-Y}{X}le frac{Ai}{X}),(left lfloor frac{Ai-X}{X} ight floorle left lfloor frac{Ai-Y}{X} ight floorle left lfloor frac{Ai}{X} ight floor),
即(left lfloor frac{Ai}{X}-1 ight floorle Ansle left lfloor frac{Ai}{X} ight floor),(left lfloor frac{Ai}{X} ight floor-1le Ansle left lfloor frac{Ai}{X} ight floor)
故(Ans=left lfloor frac{Ai}{X} ight floor-1)或(left lfloor frac{Ai}{X} ight floor), - 另:当(Ans=left lfloor frac{Ai}{X}
ight
floor-1)时,(Ai=Xcdot Ans+Y=Xcdotleft lfloor frac{Ai}{X}
ight
floor+Y-X),
又由于(Y-Xle 0,Xcdotleft lfloor frac{Ai}{X} ight floorle Ai),所以(Ans=left lfloor frac{Ai}{X} ight floor-1)时,只在(Ai\%X=0)时成立。
(注:也可以这样理解:(Ai=Xcdot Ans=Xcdot(Ans-1)+X))
综上,(Ans=left lfloor frac{Ai}{X} ight floor),特殊的,当(Ai\%X=0)时,(Ans)还有可能为((left lfloor frac{Ai}{X} ight floor-1))。
在求出(Ans)(集合大小中较小值)后,统计答案时就优先放(Ans+1),再放(Ans)这样的考虑就行了。
代码
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int MAXN=505;
int N,A[MAXN];
int St,Mi=1e9,Ans;
int Check(int ans){
for(int i=1;i<=N;i++)
if(A[i]/ans<A[i]%ans)
return 0;
return 1;
}
long long Get(){
long long ans=0;
for(int i=1;i<=N;i++){
int m=(Ans+1-A[i]%(Ans+1))%(Ans+1);//还剩m个空位需要Ans来补.
ans+=m+(A[i]-m*Ans)/(Ans+1);
}
return ans;
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&A[i]),Mi=min(Mi,A[i]);
St=sqrt(Mi)+1;Ans=St-1;
for(int i=1;i<=St;i++){
if(Check(Mi/i)){
Ans=max(Ans,Mi/i);
break;
}
if(Mi%i==0)
if(Check(Mi/i-1)){
Ans=max(Ans,Mi/i-1);
break;
}
}
printf("%lld
",Get());
}
/*
A<=(A/K)*(K+1)
ceil(A/val)-1<=K
*/