1355E - Restorer Distance 函数图像分析+三分或断点枚举

三个操作

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值