【学习笔记】动态dp
概述
动态是一类需要对的输入数据进行修改,并在修改后要快速查询的问题。
求解动态最基本的思路是分治。
一直在想动态是不是叫动态动态规划(误
例题
从题目开始说起吧,这样会比较好说。
0
可以比较直观地看出线段树的做法。
每个节点,维护一个区间和,前缀最大和,后缀最大和,和最大子段和。
然后就可以单点修改,区间查询。

#include<cstdio> #include<algorithm> #include<vector> #include<cstring> #include<queue> #include<cstdlib> using namespace std; #define N 500005 #define ll long long #define INF 0x3f3f3f3f struct node{ int sum,ms/*maxsum*/,ml,mr/*maxl,maxr*/; }tree[N*4]; int n,a[N]; inline int rd() { int f=1,x=0;char c=getchar(); while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();} while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return f*x; } void PushUp(int i) { tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum; tree[i].ml=max(tree[i<<1].sum+tree[i<<1|1].ml,tree[i<<1].ml); tree[i].mr=max(tree[i<<1|1].sum+tree[i<<1].mr,tree[i<<1|1].mr); tree[i].ms=max(max(tree[i<<1].ms,tree[i<<1|1].ms),tree[i<<1].mr+tree[i<<1|1].ml); } void build(int i,int l,int r) { if(l==r) { tree[i].sum=tree[i].ml=tree[i].mr=tree[i].ms=a[l]; return ; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); PushUp(i); } void update(int i,int pos,int val,int l,int r) { if(l==r) { tree[i].ms=tree[i].ml=tree[i].mr=tree[i].sum=val; return ; } int mid=(l+r)>>1; if(pos<=mid) update(i<<1,pos,val,l,mid); else update(i<<1|1,pos,val,mid+1,r); PushUp(i); } node query(int i,int li,int ri,int l,int r) { if(l<=li&&ri<=r) return tree[i]; int mid=(li+ri)>>1; if(r<=mid) return query(i<<1,li,mid,l,r); else if(l>mid) return query(i<<1|1,mid+1,ri,l,r); else { node x=query(i<<1,li,mid,l,r),y=query(i<<1|1,mid+1,ri,l,r),res; res.sum=x.sum+y.sum; res.ml=max(x.sum+y.ml,x.ml); res.mr=max(y.sum+x.mr,y.mr); res.ms=max(max(x.ms,y.ms),x.mr+y.ml); return res; } } int main() { n=rd(); for(int i=1;i<=n;i++) a[i]=rd(); build(1,1,n); int Q=rd(); while(Q--) { int opt=rd(); if(opt==1) { int x=rd(),y=rd(),tmp; if(x>y) tmp=x,x=y,y=tmp; printf("%d\n",query(1,1,n,x,y).ms); } else if(opt==0) { int x=rd(),y=rd(); update(1,x,y,1,n); } } return 0; }
1
那么动态是怎么做的呢
先不考虑修改,我们来想想一个正常的怎么来求全局最大子段和。
定义状态表示以结尾的最大子段和,表示的最大子段和
转移如下:
重定义矩阵乘法:把重定义为,重定义为取
即:
把式子写成矩阵的形式(设转移矩阵为
可以算出转移矩阵:
然后求的值就是一个区间矩阵乘法,可以用线段树维护(还是要用线段树

#include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<iostream> #include<cstdio> using namespace std; #define N 500005 #define INF 0x3f3f3f3f #define LL long long int rd() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();} return f*x; } int n,a[N]; struct Matrix{ int m[4][4]; Matrix(){ memset(m,0,sizeof(m)); } int* operator [](int i){ return m[i]; } }; Matrix operator * (Matrix a,Matrix b) { Matrix c; for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) c[i][j]=-INF; for(int i=1;i<=3;i++) for(int k=1;k<=3;k++) for(int j=1;j<=3;j++) c[i][j]=max(c[i][j],a[i][k]+b[k][j]); return c; } Matrix tree[N<<2]; void PushUp(int i) { tree[i]=tree[i<<1]*tree[i<<1|1]; } void Build(int i,int l,int r) { if(l==r) { tree[i][1][1]=tree[i][1][3]=tree[i][2][1]=tree[i][2][3]=a[l]; tree[i][1][2]=tree[i][3][1]=tree[i][3][2]=-INF; tree[i][2][2]=tree[i][3][3]=0; return ; } int mid=(l+r)>>1; Build(i<<1,l,mid); Build(i<<1|1,mid+1,r); PushUp(i); } void Modify(int i,int l,int r,int pos,int val) { if(l==r) { tree[i][1][1]=tree[i][1][3]=tree[i][2][1]=tree[i][2][3]=val; return ; } int mid=(l+r)>>1; if(pos<=mid) Modify(i<<1,l,mid,pos,val); else Modify(i<<1|1,mid+1,r,pos,val); PushUp(i); } Matrix Query(int i,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr) return tree[i]; int mid=(l+r)>>1; if(qr<=mid) return Query(i<<1,l,mid,ql,qr); else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr); return Query(i<<1,l,mid,ql,qr)*Query(i<<1|1,mid+1,r,ql,qr); } int main() { n=rd(); for(int i=1;i<=n;i++) a[i]=rd(); Build(1,1,n); int Q=rd(); while(Q--) { int opt=rd(),x=rd(),y=rd(); if(opt==0) { a[x]=y; Modify(1,1,n,x,y); } else { Matrix ans=Query(1,1,n,x,y); printf("%d\n",max(ans[2][1],ans[2][3]));// } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现