GSS3
Description
动态维护最大子段和,支持单点修改。
Solution
设 \(f[i]\) 表示以 \(i\) 为结尾的最大子段和, \(g[i]\) 表示 \(1 \sim i\) 的最大子段和,那么
\[f[i] = max(f[i - 1] + a[i], a[i])\]
\[g[i] = max(g[i - 1], f[i])\]
发现只跟前一项有关。我们希望使用矩阵乘法的思路,但是矩阵乘法通常只能适用于递推问题。因此我们引入广义矩阵乘法。
矩阵乘法问题可分治的原因在于矩阵乘法满足结合律,而满足结合律的根本原因是乘法对加法满足分配率,即
\[a\cdot (b+c) = a\cdot c + b\cdot c\]
那么在这里,很容易发现,加法运算对\(Min/Max\)运算也是满足分配率的,即
\[a + min(b,c) = min(a+c,b+c)\]
\[a + max(b,c) = max(a+c,b+c)\]
所谓广义矩阵乘法,就是将矩阵乘法中的加法运算换成\(Min/Max\)运算,乘法运算换成加法运算,那么这样的矩阵乘法仍然满足结合律。
考虑到 \(g[i]\) 从 \(f[i]\) 转移过来的那一项可以直接拆开,很容易得到转移方程
\[ \begin{bmatrix} f_{i} \\ g_{i} \\ 0 \end{bmatrix} = \begin{bmatrix} a_{i} & -\infty & a_{i} \\ a_{i} & 0 & a_{i}\\ -\infty & -\infty & 0 \\ \end{bmatrix} \cdot \begin{bmatrix} f_{i-1} \\ g_{i-1} \\ {0} \end{bmatrix} \]
可以将其记为
\[F_i = A_i \cdot F_{i-1}\]
于是我们用线段树暴力维护所有\(A_i\)的乘积即可。复杂度\(O(27n \log{n})\)
#include <bits/stdc++.h> using namespace std; #define int long long struct Matrix { int n,m,a[5][5]; Matrix() { n=m=0; for(int i=0;i<4;i++) for(int j=0;j<4;j++) a[i][j]=0; } Matrix operator * (const Matrix &y) { Matrix r; if(m!=y.n) return r; r.n = n; r.m = y.m; for(int i=1;i<=n;i++) { for(int j=1;j<=y.m;j++) { for(int k=1;k<=m;k++) { if(k==1) r.a[i][j]=a[i][k]+y.a[k][j]; else r.a[i][j]=max(r.a[i][j],a[i][k]+y.a[k][j]); } } } return r; } }; Matrix make(int x) { Matrix r; r.m=r.n=3; r.a[1][1]=r.a[1][3]=r.a[2][1]=r.a[2][3]=x; r.a[2][2]=r.a[3][3]=0; r.a[1][2]=r.a[3][1]=r.a[3][2]=-1e+9; return r; } const int N = 1000005; Matrix val[N],zero; int n,q,src[N],t1,t2,t3; void pushup(int p) { val[p] = val[p*2]*val[p*2+1]; } void build(int p,int l,int r) { if(l==r) { val[p]=make(src[l]); } else { build(p*2,l,(l+r)/2); build(p*2+1,(l+r)/2+1,r); pushup(p); } } void modify(int p,int l,int r,int pos,int key) { if(l==r) { val[p]=make(key); } else { if(pos<=(l+r)/2) modify(p*2,l,(l+r)/2,pos,key); else modify(p*2+1,(l+r)/2+1,r,pos,key); pushup(p); } } Matrix query(int p,int l,int r,int ql,int qr) { Matrix R=make(-1e+9); if(l>qr||r<ql) return R; if(l>=ql&&r<=qr) return val[p]; return query(p*2,l,(l+r)/2,ql,qr)*query(p*2+1,(l+r)/2+1,r,ql,qr); } signed main() { ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>src[i]; cin>>q; zero.n=3; zero.m=1; zero.a[1][1]=zero.a[2][1]=-1e+9; build(1,1,n); for(int i=1;i<=q;i++) { cin>>t1>>t2>>t3; if(t1==0) { modify(1,1,n,t2,t3); } else { Matrix r=query(1,1,n,t2,t3)*zero; cout<<r.a[2][1]<<endl; } } }
当然似乎这个问题用线段树暴力又短又快
#include <bits/stdc++.h> using namespace std; int src[1000005],a[1000005],al[1000005],ar[1000005],s[1000005],n,m,t1,t2,t3,t4; struct Result { int a,al,ar,s; }; void build(int p,int l,int r) { if(l==r) a[p]=al[p]=ar[p]=s[p]=src[l]; else { build(p<<1,l,(l+r)/2), build(p<<1|1,(l+r)/2+1,r); a[p]=max(max(a[p<<1],a[p<<1|1]),max(max(ar[p<<1],al[p<<1|1]),ar[p<<1]+al[p<<1|1])); al[p]=max(al[p<<1],s[p<<1]+max(0,al[p<<1|1])); ar[p]=max(ar[p<<1|1],max(0,ar[p<<1])+s[p<<1|1]); s[p]=s[p*2]+s[p*2+1]; } } void modify(int p,int l,int r,int pos,int key) { if(l==r) a[p]=al[p]=ar[p]=s[p]=key; else { if(pos<=(l+r)/2) modify(p<<1,l,(l+r)/2,pos,key); else modify(p<<1|1,(l+r)/2+1,r,pos,key); a[p]=max(max(a[p<<1],a[p<<1|1]),max(max(ar[p<<1],al[p<<1|1]),ar[p<<1]+al[p<<1|1])); al[p]=max(al[p<<1],s[p<<1]+max(0,al[p<<1|1])); ar[p]=max(ar[p<<1|1],max(0,ar[p<<1])+s[p<<1|1]); s[p]=s[p*2]+s[p*2+1]; } } Result query(int p,int l,int r,int ql,int qr) { Result res; res.a=-1e+8; res.al=-1e+8; res.ar=-1e+8; res.s=-1e+8; if(l>qr||r<ql) return res; if(l>=ql&&r<=qr) { res.a=a[p]; res.al=al[p]; res.ar=ar[p]; res.s=s[p]; return res; } else { Result cl,cr; cl=query(p<<1,l,(l+r)/2,ql,qr); cr=query(p<<1|1,(l+r)/2+1,r,ql,qr); res.a=max(max(cl.a,cr.a),max(max(cl.ar,cr.al),cl.ar+cr.al)); res.al=max(cl.al,cl.s+max(0,cr.al)); res.ar=max(cr.ar,max(0,cl.ar)+cr.s); res.s=cl.s+cr.s; return res; } } int main(){ ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>src[i]; build(1,1,n); cin>>m; for(int i=1;i<=m;i++) { cin>>t3>>t1>>t2; if(t3) { Result res=query(1,1,n,t1,t2); cout<<res.a<<endl; } else { modify(1,1,n,t1,t2); } } }