【总结】动态dp
Part 1. 序列动态DP
题目描述
一段区间的价值的定义如下:
可在区间内取任意个数, 这些数位置不能重复 且不能相邻.
其和的最大值为这段区间的价值.
如有序列(1,-1,-2,3,4,2,-1),则区间[4,6]的价值为5。
给定一数列, 要求支持下列两种操作:
- 单点修改
- 查询给定区间价值
对于 100 % 100\% 100% 的数据, n , m ≤ 1 0 5 n,m\le 10^5 n,m≤105.
solution:
序列dp+单点修改。
设 f i , 0 f_{i,0} fi,0 表示不选第 i i i 位时最大和, f i , 1 f_{i,1} fi,1 表示选第 i i i 位时最大和
则有状态转移方程:
f i , 0 = m a x ( f i − 1 , 0 , f i − 1 , 1 ) f_{i,0}=max(f_{i-1,0},f_{i-1,1}) fi,0=max(fi−1,0,fi−1,1)
f i , 1 = f i − 1 , 0 + a i f_{i,1}=f_{i-1,0}+a_i fi,1=fi−1,0+ai
单次查询复杂度 O ( n ) O(n) O(n) ,
修改操作直接 O ( 1 ) O(1) O(1) 修改 a i a_i ai 的值。
有 m m m 次操作,算法总复杂度 O ( n m ) O(nm) O(nm)
这个转移写成矩阵就是:
[ f i − 1 , 0 f i − 1 , 1 ] × [ 0 0 a i − ∞ ] = [ f i , 0 f i , 1 ] \left[ \begin{matrix} f_{i-1,0} \\ f_{i-1,1} \end{matrix} \right] × \left[ \begin{matrix} 0 & 0 \\ a_i & -\infty \end{matrix} \right] =\left[ \begin{matrix} f_{i,0} \\ f_{i,1} \end{matrix} \right] [fi−1,0fi−1,1]×[0ai0−∞]=[fi,0fi,1]
我们把 × 变成了 + ,+ 变成了 max。
令 A i = [ 0 0 a i − ∞ ] A_i=\left[ \begin{matrix} 0 & 0 \\ a_i & -\infty \end{matrix} \right] Ai=[0ai0−∞]
初始值 f L − 1 , 0 = f L − 1 , 1 = 0 f_{L-1,0}=f_{L-1,1}=0 fL−1,0=fL−1,1=0
[ L , R ] [L,R] [L,R] 的价值 = [ 0 0 ] \left[ \begin{matrix} 0 \\ 0 \end{matrix} \right] [00] × ∏ i = L R A i \prod_{i=L}^RA_i ∏i=LRAi
若求得区间矩阵乘积,即可直接求得区间价值。
区间矩阵乘积,可用线段树维护。
#include<bits/stdc++.h>
using namespace std;
const int mxn=1e5+10;
const int INF=0x3f3f3f3f;
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
struct Matrix {
int c[2][2];
Matrix() {memset(c,0,sizeof(c));}
Matrix operator *(const Matrix &a) {
Matrix r;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) {
r.c[i][j]=-INF;
for(int k=0;k<2;k++)
r.c[i][j]=max(r.c[i][j],c[i][k]+a.c[k][j]);
}
return r;
}
};
struct SegmentTree {
Matrix sum;
}t[mxn<<2];
int n,m,val[mxn];
void up(int p) {
t[p].sum=t[p*2].sum*t[p*2+1].sum;
}
void Build(int p,int l,int r) {
if(l==r) {
t[p].sum.c[1][0]=val[l];
t[p].sum.c[1][1]=-INF;
return;
}
int mid=(l+r)>>1;
Build(p*2,l,mid),Build(p*2+1,mid+1,r);
up(p);
}
void Modify(int p,int l,int r,int x,int y) {
if(l==r) {
t[p].sum.c[1][0]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) Modify(p*2,l,mid,x,y);
else Modify(p*2+1,mid+1,r,x,y);
up(p);
}
Matrix Query(int p,int l,int r,int ql,int qr) {
if(ql<=l&&r<=qr) return t[p].sum;
int mid=(l+r)>>1;
if(qr<=mid) return Query(p*2,l,mid,ql,qr);
if(ql>mid) return Query(p*2+1,mid+1,r,ql,qr);
return Query(p*2,l,mid,ql,qr)*Query(p*2+1,mid+1,r,ql,qr);
}
int main() {
n=read(),m=read();
for(int i=1;i<=n;i++) val[i]=read();
Build(1,1,n);
Matrix ans;
for(int i=1;i<=m;i++) {
int opt=read(),x=read(),y=read();
if(opt==1) Modify(1,1,n,x,y);
else {
Matrix ans;
ans=ans*Query(1,1,n,x,y);
printf("%d\n",max(ans.c[0][0],ans.c[0][1]));
}
}
}
Part 2. 树上动态DP
给定一个长度为 n n n 的序列,你需要维护两种操作。
①查询一个区间的最大子段和;
②单点修改(即将一个位置上的数改成另一个数)
n , q ≤ 1 0 5 n , q ≤ 10^5 n,q≤105
solution:
定义 A i = [ 0 0 V i − ∞ ] A_i=\left[ \begin{matrix} 0 & 0 \\ V_i & -\infty \end{matrix} \right] Ai=[0Vi0−∞]
不难把状态转移方程写成这个样子:
[
f
v
,
0
f
v
,
1
]
×
[
f
i
,
0
′
f
i
,
0
′
f
i
,
1
′
−
∞
]
=
[
f
i
,
0
f
i
,
1
]
\left[\begin{matrix}f_{v,0} \\ f_{v,1} \end{matrix}\right]×\left[\begin{matrix}f^{'}_{i,0} & f^{'}_{i,0} \\ f^{'}_{i,1} & -\infty\end{matrix}\right]=\left[\begin{matrix}f_{i,0} \\ f_{i,1}\end{matrix}\right]
[fv,0fv,1]×[fi,0′fi,1′fi,0′−∞]=[fi,0fi,1]
对于链的情况,可以写成这个形式:
[ f x , 0 f x , 1 ] \left[ \begin{matrix} f_{x,0} \\ f_{x,1} \end{matrix} \right] [fx,0fx,1] = ∏ i ∈ s u b s t r e e x [ f i , 0 f i , 0 f i , 1 − ∞ ] \prod_{i\in substree_x}\left[\begin{matrix}f_{i,0} & f_{i,0} \\ f_{i,1} & -\infty\end{matrix}\right] ∏i∈substreex[fi,0fi,1fi,0−∞]
线段树可以维护上面那样序列上的 d p dp dp 转移,注意序列上的 d p dp dp 值是可以通过一个原始值乘上若干转移矩阵得到的。
拿到 x x x 的 f v , 0 , f v , 1 {f_{v,0},f_{v,1}} fv,0,fv,1 后,要把它转化成标准形式。
线段树已经完成了区间合并操作,可以很快得到一个单点的值。
注意这里是单点修改,只对一个儿子产生影响,所以可以在原dp值上进行转移修改。
注意 f v , 0 , f v , 1 {f_{v,0},f_{v,1}} fv,0,fv,1 不包括重儿子,重儿子在线段树上查询即可。
v a l v , 0 , v a l v , 1 val_{v,0},val_{v,1} valv,0,valv,1 和 f v , 0 , f v , 1 f_{v,0},f_{v,1} fv,0,fv,1 必须单独处理,后者是不含 s o n x son_x sonx 的前者转移而来!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mx=1e5+5;
const int INF=100000000;
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
struct matrix{
int c[2][2];
matrix() {memset(c,0,sizeof(c));}
matrix operator *(const matrix &a) {
matrix r;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) {
r.c[i][j]=-INF;
for(int k=0;k<2;k++) {
r.c[i][j]=max(r.c[i][j],c[i][k]+a.c[k][j]);
}
}
return r;
}
}val[mx],t[mx<<2];
int n,m;
int head[mx*2],to[mx*2],nxt[mx*2],cnt;
int a[mx],tp[mx],ed[mx],son[mx],fa[mx],dfn[mx],siz[mx],rnk[mx],num;
int f[mx][2];
void add(int x,int y) {to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;}
void dfs(int x,int fath) {
siz[x]=1;
for(int i=head[x];i;i=nxt[i]) {
int y=to[i]; if(y==fath) continue;
fa[y]=x,dfs(y,x),siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int topf) {
dfn[x]=++num,tp[x]=topf,rnk[num]=x;
if(son[x]) dfs2(son[x],topf);
else ed[topf]=x;
for(int i=head[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) dfs2(y,y);
}
}
void dfs3(int x,int fath) {
f[x][1]=a[x];
val[dfn[x]].c[1][0]=a[x];
for(int i=head[x];i;i=nxt[i]) {
int y=to[i]; if(y==fath) continue;
dfs3(y,x);
f[x][1]+=f[y][0],f[x][0]+=max(f[y][0],f[y][1]);
if(y!=son[x]) val[dfn[x]].c[1][0]+=f[y][0],val[dfn[x]].c[0][0]+=max(f[y][0],f[y][1]);
}
val[dfn[x]].c[0][1]=val[dfn[x]].c[0][0];
val[dfn[x]].c[1][1]=-INF;
}
void build(int p,int l,int r) {
if(l==r) {
t[p]=val[l];
return;
}
int mid=(l+r)>>1;
build(p*2,l,mid),build(p*2+1,mid+1,r);
t[p]=t[p*2]*t[p*2+1];
}
void change(int p,int l,int r,int x) {
if(l==r) {
t[p]=val[l];
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(p*2,l,mid,x);
else change(p*2+1,mid+1,r,x);
t[p]=t[p*2]*t[p*2+1];
}
matrix query(int p,int l,int r,int ql,int qr) {
if(ql<=l&&r<=qr) return t[p];
int mid=(l+r)>>1;
if(qr<=mid) return query(p*2,l,mid,ql,qr);
if(mid<ql) return query(p*2+1,mid+1,r,ql,qr);
return query(p*2,l,mid,ql,qr)*query(p*2+1,mid+1,r,ql,qr);
}
matrix ask(int x) {
return query(1,1,n,dfn[x],dfn[ed[x]]);
}
void path_change(int u,int x) {
val[dfn[u]].c[1][0]+=x-a[u];
a[u]=x;
while(u) {
matrix od=ask(tp[u]);
change(1,1,n,dfn[u]); //1. 修改 2. 把val_x的值赋给data_x
matrix nw=ask(tp[u]);
u=fa[tp[u]];
val[dfn[u]].c[0][0]=val[dfn[u]].c[0][0]-max(od.c[0][0],od.c[1][0])+max(nw.c[0][0],nw.c[1][0]);
val[dfn[u]].c[0][1]=val[dfn[u]].c[0][0];
val[dfn[u]].c[1][0]=val[dfn[u]].c[1][0]-od.c[0][0]+nw.c[0][0];
}
}
signed main() {
// freopen("data.in","r",stdin);
// freopen("own.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) {
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs(1,0),dfs2(1,1),dfs3(1,0);
build(1,1,n);
for(int i=1;i<=m;i++) {
int u=read(),x=read();
path_change(u,x);
matrix t=ask(1);
printf("%lld\n",max(t.c[0][0],t.c[1][0]));
}
}

浙公网安备 33010602011771号