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;
}

  

posted @ 2024-09-03 16:57  我微笑不代表我快乐  阅读(29)  评论(0)    收藏  举报