题意
有一个军团,其中有 n n n 种士兵,第 i i i 种士兵有 a i a_{i} ai 个,现在要把这些士兵分成 k k k 个军团。定义一个军队的战斗能力如下:
- 战斗能力初始为 0 0 0。
- 对于每种士兵,战斗能力会增加 不在一个军团 的士兵对的数量乘以 b b b。
- 但是,首领很难操控拥有大量军团的军队,所以战斗能力会减少 x ( k − 1 ) x(k-1) x(k−1)。
- 结算时,战斗能力 可能为负。
给定 n , b , x n,b,x n,b,x 和 a a a 数组,但 不知道 k k k,求出最大的战斗能力。
题解
Part.1 k k k 确定
一眼看来,可能很难入手,我们先假设 k k k 为定值。那么,我们怎么分配士兵呢?
首先,可以发现,对于增加的战斗力,它等同于所有士兵对的个数减去同一个军团的士兵对数量。假设第 i i i 个军团有 p i p_{i} pi 个士兵,战斗能力等于:
∑ ( a i ( a i − 1 ) − p i ( p i − 1 ) ) 2 \frac{\sum (a_{i}(a_{i}-1)-p_{i}(p_{i}-1))}{2} 2∑(ai(ai−1)−pi(pi−1))
假设 M = ∑ p i ( p i − 1 ) 2 M=\frac{\sum p_{i}(p_{i}-1)}{2} M=2∑pi(pi−1),现在我们希望 M M M 最小。
假设有 6 6 6 个士兵,要分散在 4 4 4 个军团里, b = 1 , x = 0 b=1,x=0 b=1,x=0。以下有两种分法:
可以发现,越平均分配,
M
M
M 越小,战斗能力越大。
现在,我们可以写出如下函数 f ( k ) f(k) f(k),表示此时的最大战斗能力:
int f(int mid){
int cnt=0;
for (int i=1; i<=n; i++){
cnt-=(a[i]%mid)*(a[i]/mid+1)*(a[i]/mid)/2;//有 a[i]%mid 个 a[i]/mid+1
cnt-=(mid-a[i]%mid)*(a[i]/mid)*(a[i]/mid-1)/2;//剩下的全是 a[i]/mid
cnt+=a[i]*(a[i]-1)/2;
}
return cnt*x-(mid-1)*y;//别忘了减少战力(见题意)
}
Part.2 三分答案
详见:三分搜索—百度
此部分只证明是否可以使用三分。
我们可以发现:
- 如果我们的 k k k 如果太大,那么最后会损失很多战力值,所以不可行。
- 如果 k k k 太小,那么士兵无法均匀分布在军团内,所以也不好。
得出的结论是: f f f 函数的返回值一定是单峰的,可以使用三分。
Part.3 代码实现
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,x,y,a[200010],sum;
int f(int mid){
int cnt=0;
for (int i=1; i<=n; i++){
cnt-=(a[i]%mid)*(a[i]/mid+1)*(a[i]/mid)/2;
cnt-=(mid-a[i]%mid)*(a[i]/mid)*(a[i]/mid-1)/2;
cnt+=a[i]*(a[i]-1)/2;
}
return cnt*x-(mid-1)*y;
}
signed main(){
int t; cin>>t;
while (t--){
cin>>n>>x>>y; sum=0;
for (int i=1; i<=n; i++){
cin>>a[i]; sum+=a[i];
}int l=1,r=1000000000;
while (l<r){
int m1=l+(r-l)/3;
int m2=r-(r-l)/3;
if (f(m1)<=f(m2)){
l=m1+1;
}else{
r=m2-1;
}
}cout<<f(l)<<"\n";
}
}