题解 CF1355E Restorer Distance

[题目链接](Restorer Distance - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

发现,操作三(移动砖块)也可以转化为先拿一个砖块(操作二),再放一个砖块(操作一),那么当 \(M\le A+R\) 时,当然尽可能使用操作三,但是当 \(M>A+R\) 时,使用操作三还不如先用操作二再用操作一,所以先对 \(M\) 做处理:\(M=\min\{M,A+R\}\),这样保证用操作三永远不劣。

如果知道最终所有砖块的高度,那么求解代价是简单的,通过猜测,发现函数是单谷函数(三分处理),最终结果确实如此,下面给出证明。

\(P\) 表示需要向上填补的砖块数,\(Q\) 表示需要向下削减的砖块数。\(A\) 表示向上填的代价,\(R\) 表示向下减的代价。\(n\) 表示砖块总数,\(H\) 表示最终砖块到达的高度,\(h_i\) 表示所有砖块的初始高度,\(W\) 表示总代价。

我们发现,对应不同情况,总代价有以下两种情况(注:式子经过恒等变形,需要进行一步推导):

\[W = \begin{cases} Q(M-A)+PA, & \text{if $P\ge Q$} \\ P(M-R)+QR, & \text{if $P\le Q$} \end{cases} \]

容易发现,代价分成两端,不妨分开证明:

\(P\ge Q\)

我们想要发现函数的增减性,不妨考虑相邻函数值的差值(即函数的增量)。

\(H\rightarrow H+1\),记 \(P\) 的增加量 \(\Delta P\),记 \(Q\) 的减小量 \(\Delta Q\)。注意:此时一定有 \(\Delta P+\Delta Q=n\)

那么 \(\Delta P=(h_i\le H)\)(由于原来在高度及以下,现在高度加一,显然会导致这些需要向上增的加一),同理 \(\Delta Q=(h_i>H)\)

那么原来贡献 \(W=Q(M-A)+PA\) 变为 \(W'=(Q-\Delta Q)(M-A)+(P+\Delta P)A\),考虑 \(W\) 的增量,

\[\Delta W=W'-W=\Delta PA-\Delta Q(M-A)=An-M\Delta Q \]

不难发现,此处 \(\Delta Q\) 是不增的(感性上直接理解为减即可),那么 \(\Delta W\) 就是增的。

所以我们证明了,在 \(P\ge Q\) 时,随着 \(H\) 增加函数值 \(W\) 增。

\(P<Q\)

同上可以证明,在这个区间内 \(W\) 减。


不妨思考一个问题:当 \(H\) 增加,\(P\)\(Q\) 分别怎么变化。

\(H\) 特别小的时候,\(P\) 肯定很小,\(Q\) 肯定很大,而当 \(H\) 慢慢增加,\(P\) 会逐渐变大,\(Q\) 会逐渐变小,最终在 \(P=Q\) 是时候发生转折,从 \(P<Q\) 的情况过渡为 \(P\ge Q\) 的情况。

所以整个函数一定是先降后增,即单谷函数。

但三分还需要满足一个条件,即:非极值点处不能出现平台

出现平台则 \(\Delta W=0\)

那么由于当 \(\Delta W\) 是单调的(上文证明),例如对于 \(P\ge Q\) 的情况来讲,\(\Delta W\) 单调不降,也就是说,若 \(\Delta W=0\),只有可能在极值点处取。所以便证明了 平台只有可能在极值点处取

这样我们便证明了三分正确性。

总结:

  • 证明函数增减考虑相邻差值;
  • 对于分段函数证明,如果需要证明函数是单谷,则考虑证明两个分段函数增减性不同;
  • 对于多变量,考虑找出变量与常量关系。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=1e5+10;
int n,a,r,m;
int h[N];

LL calc(int H) {
    LL s1=0,s2=0;
    //s1需要往上加,s2需要往下减
    for(int i=1;i<=n;i++) {
        if(h[i]>H) s2+=h[i]-H;
        if(h[i]<H) s1+=H-h[i];
    }
    if(s1>s2) return s2*m+(s1-s2)*a;
    else return (s2-s1)*r+s1*m;
}

int main(){
    cin>>n>>a>>r>>m;
    m=min(m,a+r);
    int l=0,r=0;
    for(int i=1;i<=n;i++) {
        cin>>h[i];
        r=max(r,h[i]);
    }
    while(r-l>=3) {
        int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
        if(calc(rmid)>calc(lmid)) r=rmid;
        else l=lmid;
    }
    cout<<min({calc(l),calc(l+1),calc(l+2)});
    return 0;
}
posted @ 2024-07-09 10:43  2017BeiJiang  阅读(3)  评论(0编辑  收藏  举报