https://codeforces.com/contest/1406/problem/D
题意:给一段序列ai,构造一个bi和ci,使得bi+ci=ai,且bi单调不减,ci单调不增,求最小的max(bi,ci);多次修改[l,r]上的数+=d,再问max(ai,bi)
思路:初看此题,比较明显的是要差分维护,但是这题的关键是数学推导。
比如样例a=2,-1,7,3;
此时ai-ai-1的差分为-3,8,-4;
第一步:
设b1=x,c1=y;可以得到
b1<=b2<=b3<=b4.....<=bn;c1>=c2>=c3>=c4...>=cn;
答案在bn和c1中取一个较大的。
这时候发现,由于b是单调递增的,由差分可得,后面的数比如b2,在b1的基础上给的是正差分。c是单调递减的,后面的数比如c2,在c1的基础上给的是负差分。
比如:x+y=a1;
c2=c1+(-3);b2=b1的时候,这时候满足了题目的b2+c2==a2的条件,同时满足b2和c2的单调性条件。
当到了8这个正差分的时候,会发现如果把8拆一下比拆成-4和12,然后分别给b2+12,c2-4,这样只会让后面的bn更大。比直接把这个8给b2,c2给0来得更大。
所以现在推出来正差分给b,负差分给c.
那么有 x<=x<=x+8<=x+8
y>=y-3>=y-3>=y-7;
ans=min(x+8,y);由于x+y==a[1]=2;
所以ans=min(x+8,2-x)当两者相等取最小,x+8=2-x;x=(a1-d)/2;
这个8就是正差分的和,这个2就是a[1]。
那么就是说维护序列的a[1]和正差分之和就可以了。
tot为正差分之和
LL x=(cnt[1]-tot)/2;
cout<<max(x+tot,cnt[1]-x)<<endl;
然后碰到了修改区间时候,区间内部的差分是不变的(同加d)。只有在交界处cnt[l]和cnt[r+1]的时候谈论一下d的正负对tot的影响,然后更新。
参考了博客:https://blog.csdn.net/qq_20252251/article/details/108558104
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef long long LL;
LL a[maxn],cnt[maxn];
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n;cin>>n;
LL tot=0;//正差分之和
for(LL i=1;i<=n;i++) cin>>a[i];
for(LL i=1;i<=n;i++) cnt[i]=a[i]-a[i-1];//cnt[1]是a[1]
for(LL i=1;i<=n;i++){
if(cnt[i]>0&&i!=1) tot+=cnt[i];
}
LL x=(cnt[1]-tot)/2;
cout<<max(x+tot,cnt[1]-x)<<endl;
LL q;cin>>q;
while(q--)
{
LL l,r,d;cin>>l>>r>>d;
if(l==1) cnt[1]+=d;
else{
//如果调整的值是负数,考虑调整到的差分有没有正变负
if(d<0){
if(cnt[l]>0) tot-=min(cnt[l],-d);
}
else{
//正数调整正差分
if(cnt[l]+d>=0) tot+=min(cnt[l]+d,d);
}
cnt[l]+=d;//更新
}
if(r+1<=n){
if(d>0){
if(cnt[r+1]>0) tot-=min(cnt[r+1],d);
}
else{
if(cnt[r+1]-d>=0) tot+=min(cnt[r+1]-d,-d);
}
cnt[r+1]-=d;
}
x=(cnt[1]-tot)/2;
cout<<max(x+tot,cnt[1]-x)<<endl;
}
return 0;
}