题不难,主要是想整理一个好的三分模板。
题目大意:
n个数,三种操
作
1. 某个数+1,花费A
2. 某个数-1 , 花费R
3. 某个数-1,给另一个+1 花费M
为都为某个数的最小花费。
题目思路:
挺明显是要三分,再怎么也是把高的削减,把低的增高。
三分的判断就是,判断是否A+R>=M,选择是否要进行操作3的挪法。
很久灭有打三分,发现按照以前的打法,总是出点问题。如下
ll mid1 = (l+r)/2;
ll mid2 = (mid1+r)/2;
这样选取mid1,mid2会出问题,比如L = 1 , R = 3 并且单调递减的话。此时发现mid1 = mid2 = 2
那么 check(mid1) 就会等于 check(mid2) 那么问题来了,是选取l = mid1+1 呢还是 r = mid2-1 呢。 答案显然是都不可以,因为也有单调递增的情况。
这是我们就需要换划分区间方式,避免出现这种现象,很明显,只有最后【l,r】缩到了三个数才会这样。
ll mid1 = l+(r-l)/3;
ll mid2 = r-(r-l)/3;
改成上述方法之后,我们发现mid1 = 1 , mid2 = 3 , 此时,发现check(mid1)和check(mid2)不一样了,于是就顺利选择了下一次的区间。
#include<bits/stdc++.h>
#include<math.h>
#define ll long long
using namespace std;
const ll MAXN = 2e6+5;
const ll mod = 1e9+7;
ll a[MAXN],sum[MAXN];
ll n,A,R,M;
ll check(ll x){
ll ret = 0;
ll idx2 = upper_bound(a+1,a+n,x)-a;
ll idx1 = lower_bound(a+1,a+n,x)-a;
// cout<<idx1<<" **** "<<idx2<<endl;
ll pre = (idx1-1)*x-sum[idx1-1];
ll nxt = (sum[n]-sum[idx2-1])-(n-idx2+1)*x;
//cout<<pre<<" * "<<nxt<<endl;
if(pre > nxt){
if(A+R>=M){
ret += M*nxt;
pre -= nxt;
nxt = 0;
}
ret += A*pre + R*nxt;
}
else{
if(A+R>=M){
ret += M*pre;
nxt -= pre;
pre = 0;
}
ret += A*pre+R*nxt;
}
return ret;
}
int main()
{
cin>>n>>A>>R>>M;
for(ll i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
ll l = 1ll<<62,r = 0;
for(ll i=1;i<=n;i++){
l = min(l,a[i]);
r = max(r,a[i]);
sum[i] = sum[i-1]+a[i];
}
ll ans = 1ll<<62;
ll ck1,ck2;
while(l<=r){
ll mid1 = l+(r-l)/3;
ll mid2 = r-(r-l)/3;
ck1 = check(mid1);
ck2 = check(mid2);
//cout<<mid1<<" + "<<mid2<<" * "<<ck1<<" * "<<ck2<<endl;
if(ck1 > ck2){
l = mid1+1;
ans = min(ans,ck2);
}
else{
r = mid2-1;
ans = min(ans,ck1);
}
}
cout<<min(ck1,ck2)<<endl;
}