solution:
数据结构毒瘤题。
算法一:考虑不带修改。首先分析出一个性质,最优区间一定是单调区间。因为 f(l,r)≤max(l,r)−min(l,r)f(l,r)\leq max(l,r)-min(l,r)f(l,r)≤max(l,r)−min(l,r) ,如果一个区间不是单调区间的话可以继续拆分直到都是单调区间,从而使得 f(l,r)≥max(l,r)−min(l,r)f(l,r)\geq max(l,r)-min(l,r)f(l,r)≥max(l,r)−min(l,r) 。设 f[i]f[i]f[i] 表示前 iii 个数贡献的最大值,可以 O(n)O(n)O(n) 求出,区间 (l,r](l,r](l,r] 的贡献就是 a[r]−a[l+1]a[r]-a[l+1]a[r]−a[l+1] 。加上修改的话就暴力修改,时间复杂度 O(n2)O(n^2)O(n2) 。
算法二:考虑维护差分数组,将区间修改转化为单点修改。现在对于单调区间 [l,r][l,r][l,r] ,其贡献等于 ∑i=lrc[i]\sum_{i=l}^rc[i]∑i=lrc[i] 。进一步地,总贡献等于 ∑i=1nc[i]\sum_{i=1}^nc[i]∑i=1nc[i] ,所以我们只需要维护差分数组的和即可。规定 c[1]=0c[1]=0c[1]=0 。当然实际计算是求的绝对值的和。
一个坑点在于,len=1len=1len=1 的情况是没有贡献的,比较难判断。所以我们必须调整我们的策略:转化为在差分序列中选择一些数,使得选择的数中不存在相邻两个数,一个为正,一个为负。证明也比较容易,考虑 c[i]c[i]c[i] 能产生贡献当且仅当 [i,i+1][i,i+1][i,i+1] 在同一区间,如果有两个相邻的线段不能在同一集合的话就不能都产生贡献,反之则能。
现在我们可以用线段树维护了。dp[t][0/1][0/1]dp[t][0/1][0/1]dp[t][0/1][0/1] 表示节点 ttt 的 左/右 端点 不选/选 时贡献的最大值。时间复杂度 O(24(n+q)logn)O(2^4(n+q)logn)O(24(n+q)logn) 。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int mx=2e5+5;
int n,q;
ll dp[mx<<2][2][2];
ll a[mx],c[mx];
void PushUp(int p,int md) {
for(int i=0;i<2;i++) {
for(int j=0;j<2;j++) {
dp[p][i][j]=-INF;
for(int k=0;k<2;k++) {
for(int l=0;l<2;l++) {
if(k==1&&l==1&&(c[md]<0&&c[md+1]>0||c[md]>0&&c[md+1]<0)) continue;
dp[p][i][j]=max(dp[p][i][j],dp[p<<1][i][k]+dp[p<<1|1][l][j]);
}
}
}
}
}
void update(int p,int l,int r,int x) {
if(l==r) {
dp[p][1][1]=abs(c[l]);
return;
}
int mid=l+r>>1;
if(x<=mid) update(p<<1,l,mid,x);
else update(p<<1|1,mid+1,r,x);
PushUp(p,mid);
}
ll query() {
return max(max(dp[1][0][0],dp[1][0][1]),max(dp[1][1][0],dp[1][1][1]));
}
int main() {
// freopen("data.in","r",stdin);
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),c[i]=(i==1)?0:a[i]-a[i-1],update(1,1,n,i);
for(int i=1;i<=q;i++) {
int l,r,x; scanf("%d%d%d",&l,&r,&x);
if(l!=1) {
c[l]+=x;
update(1,1,n,l);
}
if(r!=n) {
c[r+1]-=x;
update(1,1,n,r+1);
}
printf("%lld\n",query());
}
}