E. Restorer Distance(三分查找) Codeforces Round #643 (Div. 2)

题不难,主要是想整理一个好的三分模板。

题目大意:

      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
#include
#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< 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< ck2){
            l = mid1+1;
            ans = min(ans,ck2);
        }
        else{
            r = mid2-1;
            ans = min(ans,ck1);
        }
    }
    cout<

 

你可能感兴趣的:(E. Restorer Distance(三分查找) Codeforces Round #643 (Div. 2))