【题解】CF1706C Qpwoeirut And The City

【题解】CF1706C Qpwoeirut And The City

题意简述

  • 给出一个长度为 \(n\) 的序列 \(h\),其中的每一项代表第 \(i\) 座建筑物的高度。

    \(h_i > h_{i-1}\)\(h_i>h_{i+1}\) 时,称第 \(i\) 座建筑是“凉爽的” 。

    注意,第一座建筑和最后一座建筑不可能为“凉爽的”。

  • 你可以增高任何一座建筑物的高度,使得最终“凉爽的”建筑物最多。

  • 为了使“凉爽的”建筑物最多,求出需要增加的最小总高度。

  • 多测。 \(3 \leq n \leq 10^5,1\leq h_i \leq 10^9\)

算法分析

首先考虑在什么情况下“凉爽的”建筑物是最多的。

由于“凉爽的”建筑的定义,我们知道不会存在连续的“凉爽”建筑。

由于第一座和最后一座建筑一定不是“凉爽的”,所以最终“凉爽的”建筑数为 \(\left\lceil \dfrac{n}{2} \right\rceil - 1\)

事实上,这个结果实际上是分类讨论得出的:

  1. \(n\) 为偶数,可知最多有 \(\dfrac{n-2}{2}\) 座建筑是“凉爽的”。
  2. \(n\) 为奇数,可知最多有 \(\dfrac{n-2+1}{2}\) 座建筑是“凉爽的”。

这个结论过于显然,就不举例了。

下一步考虑应该令哪些建筑物是“凉爽的”。

对于 \(n\) 是奇数的情况,“凉爽的”建筑的位置是固定的。令这些位置的建筑的高度高于两侧建筑高度的最大值即可。

对于 \(n\) 为偶数的情况,情况会稍微复杂一些:

这里令 \(1\) 代表“凉爽的”建筑,令 \(0\) 代表非“凉爽的”建筑。

在纸上写一写就会发现,符合题意的序列并不是唯一的。

比如 \(n=8\) 时就有如下几种情况:

- 1 0 1 0 1 0 -
- 1 0 1 0 0 1 -
- 1 0 0 1 0 1 -
- 0 1 0 1 0 1 -

但是,通过观察这四个序列,我们能发现一些规律:

  1. 去掉第一项和最后一项,中间的每两个分成一组,每组一定是 1 00 1 其中之一。

  2. 对于这些组,一定是前一部分是 1 0 ,后一部分是 0 1 ,并且仅会在整个序列中间更改一次顺序。

    因为 \(1\) 的左右两边一定是 \(0\) ,所以 0 1 后面不能接 1 01 0 前面也不能是 0 1

所以我们可以事先求出全是 1 0 的花费前缀和,求出全是 0 1 的花费后缀和,最后枚举在第 \(k\) 组改变了顺序求最小值即可。

对于每个测试用例,复杂度为 \(O(n)\)

参考代码

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

const int maxN = 2e5;
typedef long long ll;

int B[maxN];
//一定别忘了开long long
ll A1[maxN];
ll A2[maxN];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int N;
        scanf("%d",&N);
        for(int i = 1;i<=N;i++) scanf("%d",&B[i]);
        ll ans = 0;
        if(N & 1){ // N为奇数
            for(int i = 2;i<N;i+=2){
                int M = max(B[i-1],B[i+1]);
                if(B[i] <= M){
                    ans += (M+1-B[i]);
                }
            }
        }else{
            memset(A1,0ll,sizeof(A1));
            memset(A2,0ll,sizeof(A2));
            
            int cnt = 1;//第 cnt 组
            for(int i = 2;i<N-1;i+=2){
                int M = max(B[i-1],B[i+1]);
                if(B[i] <= M){
                    A1[cnt] = A1[cnt-1] + (M+1-B[i]);
                }else{
                    A1[cnt] = A1[cnt-1];
                }//求情况1增高的前缀和
                cnt++;
            }
            cnt = (N>>1)-1;
            for(int i = N-1;i>2;i-=2){
                int M = max(B[i-1],B[i+1]);
                if(B[i] <= M){
                    A2[cnt] = A2[cnt+1] + (M+1-B[i]);
                }else{
                    A2[cnt] = A2[cnt+1];
                }//求情况2增高的后缀和
                cnt--;
            }
            int r = (N >> 1);//一共有 (N/2)-1 组,但是考虑到下面的循环边界就加了一
            ans = 1e17;//最大值要足够大。。。
            for(int i = 1;i<=r;i++){
                //枚举在哪组变序
                ans = min(ans,A1[i-1]+A2[i]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2022-07-19 22:25  Burnling  阅读(45)  评论(0编辑  收藏  举报