# 题目

1661845999437

11
3 1 2 0 0 2 0 4 5 0 2
14
5 1 20 10 10 10 10 15 10 20 1 5 10 15
3
1 0 0
3
0 0 0

# 理解

将数组中值的大小理解为山峰的高度,pp 为水位的高度,

pp 的由大变小的变化过程理解为水位的高度由高变低的过程

根据小于pp 的被淹没,如果认为是水位由低变高,需要计算初始时非零段个数。并且对于连峰,如 [0, 8, 6, 8, 0] ,开始为 1,后来为 2,后来为 0。当pp 取 9 时会淹没全部山峰,但是 9 不是山峰的取值。导致计算上出现麻烦。而如果假设初始时水位为无穷大,水位由高到低进行变化,当水位降为 8 时,山峰个数增减,在利用前缀和的基础上,将水位(即pp 的取值)直接作为数组下标。

# 解答

/* CCF202109-2 非零段划分 */
#include <iostream>
#include <stdio.h>
#include <algorithm>
#define aaa
using namespace std;
const int N = 500000;
const int M = 10000;
int a[N + 2], d[M + 1];
/*
* 这里还需要注意的是:是假设水从上面下降,逐渐显现出山峰;而不是水位逐渐升高,淹没山峰
* 因为用下降来处理的话,最后才计算的 0 是可以不考虑的。(求前缀和时最后计算的 0)
* 如果用水位上升来处理,不能不考虑 0?
* 
* 因为题意的关系,是小于 p 的置为 0,而在求算时 p 的取值是数组中的某个数,
* 在水位上升时,p 取到山峰时,非零段的数目是不变的;p 大于山谷一个 1 的时候,非零段的数目才增 1
* 上升关系不能用差分来做
*/
int main()
{
#ifdef aaa
    freopen("stdin.txt", "r", stdin);
#endif // aaa
    int n;
    scanf("%d", &n);
    // 开始位置为 1 上
    for (int i = 1; i <= n; i++) 
        scanf("%d", &a[i]);
    a[0] = a[n + 1] = 0;
    //a+n+2 中的 2 是因为上面设置了 a [0] = a [n + 1] = 0,
    // 最后还要再减去 1 是因为 a [0] 么?那最终的 n 代表的意义是什么?
    //unique (a, a + n + 2) - a 表示去重后的数组的元素个数(设为 m,则 a 数组下标取到的最大值为 m-1,查看下面的 i+1 的最大值即是这个),因为在下面的 for 循环中用到了 i+1,
    //i+1<unique (a, a + n + 2) - a,即 i<unique (a, a + n + 2) - a - 1
    // 故 n 的取值如此
    n = unique(a, a + n + 2) - a - 1;
    memset(d, 0, sizeof d);
    //d 数组下标的取值就是 p 值,这里是假设水从上往下下降的
    for (int i = 1; i < n; i++)
        if (a[i - 1] < a[i] && a[i] > a[i + 1]) 
            d[a[i]]++; 
            // 只有当它比两边都高的时候,当 p 取为 a [i] 时,它作为新的山峰显现出来;如果旁边有更高的,并不会出现新的山峰
        else if (a[i - 1] > a[i] && a[i] < a[i + 1]) 
            d[a[i]]--;
            // 如果 a [i] 为 0 呢?
            //a [i] 为 0 时必然会有 d [0]--,但是在下面求前缀和的时候不会用到 d [0]
    int ans = 0, sum = 0;   // 差分前缀和即为答案
    //M 为可能的最大数,水从上往下下降的
    for (int i = M; i >= 1; i--)
        sum += d[i], ans = max(ans, sum);
    printf("%d\n", ans);
    return 0;
}