概述
动态$dp$是一类需要对$dp$的输入数据进行修改,并在修改后要快速查询的问题。
求解动态$dp$最基本的思路是分治。
一直在想动态$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; }