nlfsoj 20035 #10851 最大連續子段和 SP1716 GSS3 - Can you answer these queries III
\(n\) 個數,\(q\) 個操作
操作 \(0\ x\ y\) 表示把 \(a_x\) 修改為 \(y\)
操作 \(1\ l\ r\) 詢問 \([l,r]\) 的最大子段和
\(1\leq n,q\leq 50000,|y|\leq 10000\)
sol1
線段樹做法 .
每個節點維護,字首最大值 \(a\),字尾最大值 \(b\),區間內最大連續子段和 \(c\) , 區間和 \(w\) .
只有單點查詢,所以只需要考慮合併操作 .
將左區間 \(l\) 和右區間 \(r\) 合併到 \(x\) .
\(x.a=\max(l.a,l.w+r.a)\) 字首最大值在左區間 \(l.a\) ,在右區間 \(l.w+r.a\)
\(x.b=\max(r.b,r.w+l.b)\) 字尾最大值在右區間 \(r.b\) ,在左區間 \(r.w+l.b\)
\(x.c=\max(l.c,r.c,l.b+r.a)\)
區間最大連續子段和在左區間 \(l.c\),在右區間 \(r.c\),兩個區間都有,即 \(l.b+r.a\)
\(x.w=l.w+r.w\)
知道了合併操作,區間求和,單點修改就都不在話下了 .
時間複雜度 : \(\mathrm O(q\log n)\)
空間複雜度 : \(\mathrm O(n)\)
code
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch=getchar(); while((ch<'0'||ch>'9')&&(ch!='-'))ch=getchar(); int f=false; if(ch=='-'){ f=true; ch=getchar(); } int res=0; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } if(f)res=-res; return res; } inline void print(int res){ if(res==0){ putchar('0'); return; } if(res<0){ putchar('-'); res=-res; } int a[10],len=0; while(res>0){ a[len++]=res%10; res/=10; } for(int i=len-1;i>=0;i--) putchar(a[i]+'0'); } const int inf=1e9+10; class node{public:int a,b,c,w;}tree[50010<<2]; int a[50010]; void pu(node &x,node l,node r){ x.a=max(l.a,l.w+r.a); x.b=max(r.b,r.w+l.b); x.c=max(l.b+r.a,max(l.c,r.c)); x.w=l.w+r.w; } void build(int x,int l,int r){ if(l==r){ tree[x]=(node){a[l],a[l],a[l],a[l]}; return; } int mid=(l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); pu(tree[x],tree[x<<1],tree[x<<1|1]); } void upd(int x,int l,int r,int pos,int val){ if(l==r){ tree[x]=(node){val,val,val,val}; return; } int mid=(l+r)>>1; if(pos<=mid)upd(x<<1,l,mid,pos,val); else upd(x<<1|1,mid+1,r,pos,val); pu(tree[x],tree[x<<1],tree[x<<1|1]); } node qry(int x,int l,int r,int cl,int cr){ if(l==cl&&cr==r)return tree[x]; int mid=(l+r)>>1; if(cr<=mid)return qry(x<<1,l,mid,cl,cr); if(cl>=mid+1)return qry(x<<1|1,mid+1,r,cl,cr); node res; pu(res,qry(x<<1,l,mid,cl,mid),qry(x<<1|1,mid+1,r,mid+1,cr)); return res; } int n,q; int main(){ n=read(); for(int i=0;i<n;i++)a[i]=read(); build(1,0,n-1); q=read(); for(int i=0;i<q;i++){ int type=read(); if(type==0){ int x=read()-1,val=read(); upd(1,0,n-1,x,val); } else{ int l=read()-1,r=read()-1; print(qry(1,0,n-1,l,r).c); putchar('\n'); } } return 0; } /*inline? ll or int? size? min max?*/
sol2
\(ddp\) 做法.
考慮兩個 \(dp\) ,
\(f(i)\) 表示以 \(i\) 結束的連續子序列最大值 .
\(g(i)\) 表示區間 \([0,i]\) 的連續子序列最大值 .
考慮轉移方程,
\(f(i)=\max(a_i,f(i-1)+a_i)\)
\(g(i)=\max(f(i),g(i-1))\) 即 \(g(i)=\max(f(i),f(i-1)+a_i,a_i)\)
\(f(i),g(i)\) 都是從 \(f(i-1),g(i-1)\) 轉移過來的,考慮轉移矩陣. 這個的矩陣是廣義矩陣,依舊滿足結合律 .
\[\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} \]這個的矩陣是廣義矩陣,依舊滿足結合律 .
矩陣寫得有點奇怪,因為矩陣的計算要從下標從大往小算 .
不帶修改的時候,矩陣快速冪就可以解決 .
但是帶上修改操作,就需要用線段樹維護,每個節點維護當前區間的轉移矩陣 .
修改和查詢和普通的線段樹一樣,只是 pushup 操作是兩個矩陣相乘 .
合併的時候注意,因為我寫的轉移矩陣下表要從大往小算,所以,合併的時候是右兒子乘左兒子 .
其實也可以寫得正常一點,轉移矩陣就是
\[\begin{bmatrix} f(i-1)&g(i-1)&0 \end{bmatrix} \begin{bmatrix} a_i&a_i&-\infty\\ -\infty&0&-\infty\\ a_i&a_i&0\\ \end{bmatrix} = \begin{bmatrix} f(i),g(i),0 \end{bmatrix} \]此時下標就正常了,是從左往右合併 .
時間複雜度 : \(\mathrm O(27q\log n)\)
空間複雜度 : \(O(27n)\)
code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
while((ch<'0'||ch>'9')&&(ch!='-'))ch=getchar();
bool f=false;
if(ch=='-'){
f=true;
ch=getchar();
}
int res=0;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
if(f)res=-res;
return res;
}
inline void print(int res){
if(res==0){
putchar('0');
return;
}
if(res<0){
putchar('-');
res=-res;
}
int a[10],len=0;
while(res>0){
a[len++]=res%10;
res/=10;
}
for(int i=len-1;i>=0;i--)
putchar(a[i]+'0');
}
const int inf=1e9+10;
int v[50010<<2];
class node{
public:
int a[3][3];
}t[50010<<2];
inline node operator*(const node&x,const node&y){
node res;
for(int i=0;i<3;i++)for(int j=0;j<3;j++)
res.a[i][j]=-inf;
for(int k=0;k<3;k++){
for(int i=0;i<3;i++)for(int j=0;j<3;j++){
if(x.a[i][k]==-inf||y.a[k][j]==-inf)continue;
res.a[i][j]=max(res.a[i][j],x.a[i][k]+y.a[k][j]);
}
}
return res;
}
node pu(const node&x,const node&y){
node res;
for(int i=0;i<3;i++)for(int j=0;j<3;j++)res.a[i][j]=-inf;
res=x*y;
return res;
}
void build(int x,int l,int r){
if(l==r){
t[x].a[0][0]=v[l];t[x].a[0][1]=-inf;t[x].a[0][2]=v[l];
t[x].a[1][0]=v[l];t[x].a[1][1]=0;t[x].a[1][2]=v[l];
t[x].a[2][0]=-inf;t[x].a[2][1]=-inf;t[x].a[2][2]=0;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
t[x]=pu(t[x<<1|1],t[x<<1]);
/* for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cout<<t[x].a[i][j]<<" ";
}
cout<<endl;
}
*/
}
void upd(int x,int l,int r,int pos,int val){
if(l==r){
t[x].a[0][0]=val;t[x].a[0][1]=-inf;t[x].a[0][2]=val;
t[x].a[1][0]=val;t[x].a[1][1]=0;t[x].a[1][2]=val;
t[x].a[2][0]=-inf;t[x].a[2][1]=-inf;t[x].a[2][2]=0;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)upd(x<<1,l,mid,pos,val);
else upd(x<<1|1,mid+1,r,pos,val);
t[x]=pu(t[x<<1|1],t[x<<1]);
/* for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cout<<t[x].a[i][j]<<" ";
}
cout<<endl;
}
*/
}
node qry(int x,int l,int r,int cl,int cr){
if(cl==l&&cr==r)return t[x];
int mid=(l+r)>>1;
if(cr<=mid)return qry(x<<1,l,mid,cl,cr);
if(mid+1<=cl)return qry(x<<1|1,mid+1,r,cl,cr);
return qry(x<<1|1,mid+1,r,mid+1,cr)*qry(x<<1,l,mid,cl,mid);
}
int n,q;
int main(){
n=read();
for(int i=0;i<n;i++)v[i]=read();
build(1,0,n-1);
q=read();
for(int i=0;i<q;i++){
int type=read();
if(type==0){
int id=read()-1,val=read();
upd(1,0,n-1,id,val);
}
else{
int l=read()-1,r=read()-1;
node matrix=qry(1,0,n-1,l,r);
print(max(max(matrix.a[0][2],matrix.a[1][2]),matrix.a[0][3]));
putchar('\n');
}
}
return 0;
}
/*inline? ll or int? size? min max?*/
/*
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
*/