给定一个序列 a1,a2,…,an,长度为 n 的序列 b,c 符合以下条件
- ai=bi+ci
- b 为不下降序列,c 为不上升序列,即 bi≥bi−1,ci≤ci−1
有 q 个操作,每次操作把 [l,r] 的数增加 x。求每次操作后最小的 max(bi,ci)
看到单调不降和单调不升,就不难想到作差分。
设 adi=ai−ai−1,bdi=bi−bi−1,cdi=ci−ci−1(a0=0,b0=0,c0=0),显然对于所有 2≤i≤n,有 bdi≥0,cdi≤0。那么问题就转化为:
给定一个长度为 n 的序列 ad,构造两个长度为 n,对于所有 2≤i≤n 都满足 bdi≥0,cdi≤0 且对于所有 1≤i≤n 都满足 bdi+cdi=adi 的序列 bd,cd,使得 max(∑bdi,−∑cdi) 最小。
不难发现,因为 bdi≥0,cdi≤0,所以对于所有 adi≥0 的 i 一定有 bdi=adi,cdi=0,对于所有 adi<0 的 i 一定有 bdi=0,cdi=adi。那么不妨钦定 max(∑bdi,cd1) 一定为 cd1,那么设 sm=i=2∑nmax(adi,0),显然有 ad1−cd1+sm≤cd1,那么有 cd1≥⌈2ad1+sm⌉,所以答案就是 ⌈2ad1+sm⌉。
每次操作只会改动两个位置,那么动态维护差分即可。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=100005;
int n;
long long a[S];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=n;i>=1;i--) a[i]-=a[i-1];
long long sm=0;
for(int i=2;i<=n;i++) if(a[i]>0) sm+=a[i];
printf("%lld\n",(sm+a[1])/2+(sm+a[1]>0)*((sm+a[1])%2));
int q;
scanf("%d",&q);
while(q--)
{
int l,r;
long long x;
scanf("%d%d%lld",&l,&r,&x);
r++;
if(l>1)
{
if(a[l]>0) sm-=a[l];
a[l]+=x;
if(a[l]>0) sm+=a[l];
}
else a[l]+=x;
if(r<=n)
{
if(a[r]>0) sm-=a[r];
a[r]-=x;
if(a[r]>0) sm+=a[r];
}
printf("%lld\n",(sm+a[1])/2+(sm+a[1]>0)*((sm+a[1])%2));
}
return 0;
}