problem
给出 n n n 个数 a i a_i ai,有 q q q 次操作,操作分为以下两种:
- 0    x    y 0 \;x \;y 0xy:把 a x a_x ax 修改为 y y y。
- 1    l    r 1\; l\; r 1lr:询问区间 [ l , r ] [l, r] [l,r] 的最大子段和。
数据范围: n , q ≤ 50000 n,q \le 50000 n,q≤50000。
solution
这道题首先有一个线段树的经典做法,即维护最大前缀和、最大后缀和、最大子段和以及这段区间的和,这里不再赘述。
这篇题解是从 动态DP 的角度来做这道题的。关于动态DP,可以参考这篇博客,感觉写得非常不错。
设 f i f_i fi 表示以 a i a_i ai 结尾的最大子段和, g i g_i gi 表示 [ 1 , i ] [1,i] [1,i] 的最大子段和,那么有转移:
f i = max { f i − 1 + a i , a i } g i = max { g i − 1 , f i } = max { g i − 1 , f i − 1 + a i , a i } \begin{aligned} f_i&=\max\{f_{i-1}+a_i,a_i\}\\ g_i&=\max\{g_{i-1},f_i\}=\max\{g_{i-1},f_{i-1}+a_i,a_i\} \end{aligned} figi=max{fi−1+ai,ai}=max{gi−1,fi}=max{gi−1,fi−1+ai,ai}
我们新定义一种矩阵乘法,然后把上面的转移写成矩阵乘法的形式:
[ a i − ∞ a i a i 0 a i − ∞ − ∞ 0 ] [ f i − 1 g i − 1 0 ] = [ f i g i 0 ] \begin{bmatrix} a_i&-\infty&a_i\\ a_i&0&a_i\\ -\infty&-\infty&0\end{bmatrix} \begin{bmatrix}f_{i-1}\\g_{i-1}\\0\end{bmatrix}= \begin{bmatrix}f_i\\g_i\\0\end{bmatrix} ⎣⎡aiai−∞−∞0−∞aiai0⎦⎤⎣⎡fi−1gi−10⎦⎤=⎣⎡figi0⎦⎤
具体的定义方式还是参考那篇博客吧。
那么每一个点就可以改写成一个矩阵,用线段树维护矩阵的乘法即可,修改也十分方便。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define inf (1<<30)
using namespace std;
int n,q;
int val[N];
struct matrix{
int m[3][3];
matrix() {memset(m,128,sizeof(m));}
friend matrix operator*(const matrix &A,const matrix &B){
matrix C;
for(int i=0;i<=2;++i)
for(int k=0;k<=2;++k)
for(int j=0;j<=2;++j)
C.m[i][j]=max(C.m[i][j],A.m[i][k]+B.m[k][j]);
return C;
}
}Seg[N<<2];
void build(int root,int l,int r){
if(l==r){
Seg[root].m[0][1]=Seg[root].m[2][0]=Seg[root].m[2][1]=-inf;
Seg[root].m[0][0]=Seg[root].m[0][2]=Seg[root].m[1][0]=Seg[root].m[1][2]=val[l];
Seg[root].m[1][1]=Seg[root].m[2][2]=0;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
Seg[root]=Seg[root<<1]*Seg[root<<1|1];
}
void Modify(int root,int l,int r,int x,int y){
if(l==r){
Seg[root].m[0][0]=Seg[root].m[0][2]=Seg[root].m[1][0]=Seg[root].m[1][2]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) Modify(root<<1,l,mid,x,y);
else Modify(root<<1|1,mid+1,r,x,y);
Seg[root]=Seg[root<<1]*Seg[root<<1|1];
}
matrix Query(int root,int l,int r,int x,int y){
if(l>=x&&r<=y) return Seg[root];
int mid=(l+r)>>1;
if(y<=mid) return Query(root<<1,l,mid,x,y);
if(x>mid) return Query(root<<1|1,mid+1,r,x,y);
return Query(root<<1,l,mid,x,y)*Query(root<<1|1,mid+1,r,x,y);
}
int main(){
int op,x,y;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&val[i]);
build(1,1,n);
scanf("%d",&q);
for(int i=1;i<=q;++i){
scanf("%d%d%d",&op,&x,&y);
if(op){
matrix ans=Query(1,1,n,x,y);
printf("%d\n",max(ans.m[1][0],ans.m[1][2]));
}
else Modify(1,1,n,x,y);
}
return 0;
}