P6389. 「JOI 2021 Final」有趣的家庭菜园 4



SOl
首先是一个非常简单的结论:
对于一个严格上升的数列,其对应的差分数列,必然全是正数字
例如
原数列1 5 8 10
对应的差分数列为1 4 3 2
但如果是 1 8 3 10
对应的差分数列为 1 7 -5 7
如果我们希望将-5变成一个正数字,则给它加上6,得到数列
1 7 1 7
其对应的原数列为1 8 9 16
可看出,这其实是在将原数列(1 8 3 10)中的最后两个数字都加上了6得到的
因为原数列中第3个数字3比它左边的数字8要小,所以要加上6后,才能比8大
但为了保持其与它右边的数字10相对大小关系不变,所以要加就一起来加
这样提示我们,当找到一个数字比它左边的小,我们可以从这个位置开始,一直加到最后一个位置。
于是我们可以枚举某个位置peak,这个位置是整个数列的顶峰,即它左边,右边的数字都比它要小。
于是反映在差分数列中,在第peak个位置的左边,但凡有哪个位置j是负数,就在原数列中[j,peak]这一段统统加上某个数字
在第peak个位置的右边,但凡哪个位置j是正数,
则说明原数列中第j-1个数字是小于第J个数字的,这是不允许的
由于只有加法操作,没有减法操作,于是就在原数列从[peak,j-1]这一段统统加上某个数字
因此以PEAK为中心点,左边有一些加操作,右边也有一些加操作,这些操作有些重合的部分,是可以合并的
于是整个的运算的结果ans=max(左边加操作次数,右边加操作次数)
F[i][0]代表扫到第i个位置,保证前i个是严格上升时,最少应该加的数字
例如
4
3 9 7 2
差分后得到 D数列为
3 6 -2 -5
因为d[1]>0,于是f[1][0]=0
因为d[2]>0,于是f[2][0]=0
因为d[3]=-2<0,于是为了保证这个位置为正,得给它加上3,于是f[3][0]=0-(-2)+1=3
因为d[3]=-5<0,于是为了保证这个位置为正,得给它加上6,于是f[3][0]=3+6=9
另一个更公式化的表述

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, s=1e18, a[200010], d[200010];
int f[200010][3];
signed main(){
scanf ("%lld", &n);
for (int i=1; i<=n; i++)
{
scanf ("%lld", &a[i]);
d[i] = a[i] - a[i-1];
}
for (int i=2; i<=n; i++)
{
if (d[i] > 0)
f[i][0] = f[i-1][0];
else
f[i][0] = f[i-1][0] - d[i] + 1;
// cout<<i<<" "<<f[i][0]<<endl;
}
for (int i=n; i>=2; i--)
if (d[i] < 0)
f[i][1] = f[i+1][1];
else
f[i][1] = f[i+1][1] + d[i] + 1;
for (int i=1; i<=n; i++)
s = min(s, max(f[i][0], f[i+1][1]));
printf ("%lld\n", s);
return 0;
}

浙公网安备 33010602011771号