三个操作
1:加一块砖花费A
2:拆一块砖花费R
3:把一个柱子的一块砖移动到另一个柱子上。花费M
如果我们把M=min(M,A+R).
则如果最终高度为H,那么显然:能进行3操作就进行3操作。最后再进行1,2进行矫正。
假设有n1个需要加的砖,n2个需要拆的砖。
我们枚举最终高度H。
刚开始的时候H比较小。
显然有n1>=n2
则花费ans=(n1-n2)*A+M*n2.
假设当前有X个比最终H小的柱子。Y个大的柱子。
当H=H+1时:
如果X不变。
ans的变化为:((n1+X)-(n2-Y))*A+M*(n2-Y)- [ (n1-n2)*A+M*n2] = AN-(N-X)*M (N=X+Y)
显然,随着H上升,X不变的情况下,ans线性上升。
但H上升到等于某个hi时,再+1,则会让X--,Y++。
则:ans的上升幅度会下降(即斜率下降)但依然是单调递增。
直到AN-(N-X)*M<0时,ans会逐渐下降。
以上只是考虑n1始终大于n2的情况。
但当H=(sum{hi} / N)时,H再增加,就会出现n2>n1的情况
ans变化量变成了:RN-(N-X)*M (斜率发生改变)
无论n2>n1的情况在哪里出现,整个函数始终是先单调递增,再单调递减的状态。如下图
A点是H=(sum{hi} / N)的断点。(即使A出现在右侧,也只是会让下降幅度变大或变小,不会影响整个函数是先增后减的情况)
B点是AN-(N-X)*M=0的断点。
综上所述:
有两种解法:
1.三分H即可。(因为H-ans的函数是单峰函数,且先递增后递减)
2.枚举所有断点,其中必有一个最小值(其余部分是线性的),即枚举H等于hi的情况,最后再枚举H=(sum{hi} / N)+1
H=(sum{hi} / N)即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
ll n,a,r,m;
ll h[M];
ll gao(ll H)
{
ll n1=0,n2=0;
for(int i=1;i<=n;i++)
{
if(h[i]<=H)n1+=H-h[i];
else n2+=h[i]-H;
}
// cout<<"=-== "<<n1<<" "<<n2<<endl;
ll ans=min(a+r,m)*min(n1,n2);
// cout<<"--- "<<ans;
if(n1>n2)ans+=(n1-n2)*a;
else ans+=(n2-n1)*r;
// cout<<"=== "<<ans<<endl;
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>a>>r>>m;
for(int i=1;i<=n;i++)cin>>h[i];
ll l=1,r=1e9;
while(l<=r)
{
ll c=(r-l)/3;
ll m1=l+c,m2=r-c;
if(gao(m1)>=gao(m2))l=m1+1;
else r=m2-1;
}
// cout<<l<<" "<<r<<"-- "<<gao(l)<<" "<<gao(r)<<endl;
if(gao(l)>=gao(r))cout<<gao(r)<<endl;
else cout<<gao(l)<<endl;
return 0;
}