动态DP学习笔记
P4719 【模板】"动态 DP"
题如其名,是个板子。
考虑动态 \(\text{dp}\) 实际上就是用矩阵结合数据结构来维护 \(\text{dp}\) 值,考虑将状态转移方程用矩阵的形式写出来。
令 \(f_{u,0/1}\) 表示以 \(u\) 为根的点不选或选的最大权值。
\[f_{u,0}=\max(f_{v,0},f_{v,1})\\
f_{u,1}=f_{v,0}+a_u\\
\]
我们定义广义的矩阵乘法为,
\[c_{i,j}=\max_{k}(a_{i,k}+b_{k,i})\\
\]
那么矩阵就可以构造为,
\[\begin{bmatrix}f_{u,0}\\f_{u,1}\end{bmatrix}=\begin{bmatrix}0&0\\a_u&-\infty\end{bmatrix}\times\begin{bmatrix}f_{v,0}\\f_{v,1}\end{bmatrix}
\]
然后再考虑如果是在树上搞的话我们可以想到树剖,一条重链上的每一个点都挂了下面的子树,也就是说 \(a_u\) 和 \(0\) 要换成对应子树选或不选的值,即,
\[\begin{bmatrix}f_{u,0}\\f_{u,1}\end{bmatrix}=\begin{bmatrix}g_{u,0}&g_{u,0}\\g_{u,1}&-\infty\end{bmatrix}\times \begin{bmatrix}f_{v,0}\\f_{v,1}\end{bmatrix}
\]
实现的时候要多注意矩阵的方向,保持计算的时候矩阵方向的始终一致。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const long long INF=1e18+7;
int n,m,a[N];long long f[N][2],g[N][2];
struct Matrix{long long f[2][2];};
Matrix operator * (Matrix a,Matrix b){
Matrix res;
for(int i=0;i<2;++i){
for(int j=0;j<2;++j)
res.f[i][j]=-INF;
}
for(int i=0;i<2;++i){
for(int k=0;k<2;++k){
for(int j=0;j<2;++j){
long long tmp=a.f[i][k]+b.f[k][j];
res.f[i][j]=max(res.f[i][j],tmp);
}
}
}
return res;
}
struct Seg_Tree{
struct Node{Matrix f;}tr[N<<2];
void up(int u){
tr[u].f=tr[u<<1].f*tr[u<<1|1].f;
}
void modify(int u,int l,int r,int x,long long g0,long long g1){
if(l==r){
tr[u].f.f[0][0]=tr[u].f.f[0][1]=g0;
tr[u].f.f[1][0]=g1,tr[u].f.f[1][1]=-INF;
return void();
}
int mid=(l+r)>>1;
if(x<=mid) return modify(u<<1,l,mid,x,g0,g1),up(u);
else return modify(u<<1|1,mid+1,r,x,g0,g1),up(u);
}
Matrix query(int u,int l,int r,int x,int y){
if(x<=l&&r<=y) return tr[u].f;
int mid=(l+r)>>1;Matrix res1,res2;
if(x<=mid) res1=query(u<<1,l,mid,x,y);
if(y>mid) res2=query(u<<1|1,mid+1,r,x,y);
if(x<=mid&&y>mid) return res1*res2;
return x<=mid?res1:res2;
}
}t;
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int fa,top,bot,son,siz,dep,mp;}tr[N];int dfn[N],cnt_dfn=0;
void dfs1(int u){
tr[u].siz=1,tr[u].top=tr[u].bot=u;
tr[u].dep=tr[tr[u].fa].dep+1;
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==tr[u].fa) continue;
tr[v].fa=u,dfs1(v),tr[u].siz+=tr[v].siz;
if(tr[v].siz>tr[tr[u].son].siz) tr[u].son=v;
}
}
void dfs2(int u){
dfn[++cnt_dfn]=u,tr[u].mp=cnt_dfn;
if(tr[u].son){
tr[tr[u].son].top=tr[u].top;
dfs2(tr[u].son),tr[u].bot=tr[tr[u].son].bot;
}
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v!=tr[u].fa&&v!=tr[u].son) dfs2(v);
}
}
void work(int u){
t.modify(1,1,n,tr[u].mp,g[u][0],g[u][1]),u=tr[u].top;
if(tr[u].fa) g[tr[u].fa][0]-=max(f[u][0],f[u][1]),g[tr[u].fa][1]-=f[u][0];
Matrix tmp=t.query(1,1,n,tr[u].mp,tr[tr[u].bot].mp);
f[u][0]=max(tmp.f[0][0],tmp.f[0][1]),f[u][1]=max(tmp.f[1][0],tmp.f[1][1]);
if(tr[u].fa) g[tr[u].fa][0]+=max(f[u][0],f[u][1]),g[tr[u].fa][1]+=f[u][0],work(tr[u].fa);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) g[i][1]=a[i];
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
dfs1(1),dfs2(1);
for(int i=n;i>=1;--i){
int u=dfn[i];
t.modify(1,1,n,tr[u].mp,g[u][0],g[u][1]);
if(u==tr[u].top){
Matrix tmp=t.query(1,1,n,tr[u].mp,tr[tr[u].bot].mp);
f[u][0]=max(tmp.f[0][0],tmp.f[0][1]),f[u][1]=max(tmp.f[1][0],tmp.f[1][1]);
if(tr[u].fa) g[tr[u].fa][0]+=max(f[u][0],f[u][1]),g[tr[u].fa][1]+=f[u][0];
}
}
for(int i=1;i<=m;++i){
int x,y;scanf("%d%d",&x,&y);
g[x][1]-=a[x],a[x]=y,g[x][1]+=a[x];
work(x),printf("%lld\n",max(f[1][0],f[1][1]));
}
return 0;
}
P5024 [NOIP2018 提高组] 保卫王国
考虑到这和模板一样,我们用 \(f_{u,0}\) 来设计状态。
转移应该是和前面几乎一摸一样的(注意这里是求最小),即
\[\begin{bmatrix}f_{u,0}\\f_{u,1}\end{bmatrix}=\begin{bmatrix}\infty&g_{u,0}\\g_{u,1}&g_{u,1}\end{bmatrix}\times \begin{bmatrix}f_{v,0}\\f_{v,1}\end{bmatrix}
\]
然后我们再来考虑这个限制的问题,发现其实就是等价于两个修改,使得这两个要求的反面要求的代价为无限即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const long long INF=1e13+7;
int n,m;string s;
long long p[N][2],f[N][2],g[N][2];
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int fa,top,bot,son,siz,mp;}tr[N];int dfn[N],cnt_dfn=0;
void dfs1(int u){
tr[u].siz=1,tr[u].top=tr[u].bot=u;
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==tr[u].fa) continue;
tr[v].fa=u,dfs1(v),tr[u].siz+=tr[v].siz;
if(tr[v].siz>tr[tr[u].son].siz) tr[u].son=v;
}
}
void dfs2(int u){
dfn[++cnt_dfn]=u,tr[u].mp=cnt_dfn;
if(tr[u].son){
tr[tr[u].son].top=tr[u].top;
dfs2(tr[u].son),tr[u].bot=tr[tr[u].son].bot;
}
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v!=tr[u].fa&&v!=tr[u].son) dfs2(v);
}
}
struct Matrix{long long f[2][2];};
Matrix operator * (Matrix a,Matrix b){
Matrix res;
for(int i=0;i<2;++i){
for(int j=0;j<2;++j)
res.f[i][j]=INF;
}
for(int i=0;i<2;++i){
for(int k=0;k<2;++k){
for(int j=0;j<2;++j){
long long tmp=a.f[i][k]+b.f[k][j];
res.f[i][j]=min(res.f[i][j],tmp);
}
}
}
return res;
}
struct Seg_Tree{
struct Node{Matrix f;}tr[N<<2];
void up(int u){
tr[u].f=tr[u<<1].f*tr[u<<1|1].f;
}
void modify(int u,int l,int r,int x,long long g0,long long g1){
if(l==r){
tr[u].f.f[0][0]=INF,tr[u].f.f[0][1]=g0;
tr[u].f.f[1][0]=tr[u].f.f[1][1]=g1;
return ;
}
int mid=(l+r)>>1;
if(x<=mid) return modify(u<<1,l,mid,x,g0,g1),up(u);
else return modify(u<<1|1,mid+1,r,x,g0,g1),up(u);
}
Matrix query(int u,int l,int r,int x,int y){
if(x<=l&&r<=y) return tr[u].f;
int mid=(l+r)>>1;Matrix res1,res2;
if(x<=mid) res1=query(u<<1,l,mid,x,y);
if(y>mid) res2=query(u<<1|1,mid+1,r,x,y);
if(x<=mid&&y>mid) return res1*res2;
return x<=mid?res1:res2;
}
}t;
void work(int u){
t.modify(1,1,n,tr[u].mp,g[u][0],g[u][1]),u=tr[u].top;
if(tr[u].fa) g[tr[u].fa][0]-=f[u][1],g[tr[u].fa][1]-=min(f[u][0],f[u][1]);
Matrix tmp=t.query(1,1,n,tr[u].mp,tr[tr[u].bot].mp);
f[u][0]=min(tmp.f[0][0],tmp.f[0][1]),f[u][1]=min(tmp.f[1][0],tmp.f[1][1]);
if(tr[u].fa) g[tr[u].fa][0]+=f[u][1],g[tr[u].fa][1]+=min(f[u][0],f[u][1]),work(tr[u].fa);
}
void change(int x,int y,long long z){
g[x][y]-=p[x][y],g[x][y]+=(p[x][y]=z),work(x);
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=n;++i){
scanf("%lld",&p[i][1]),p[i][0]=0;
g[i][1]=p[i][1],g[i][0]=p[i][0];
}
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
dfs1(1),dfs2(1);
for(int i=n;i>=1;--i){
int u=dfn[i];
t.modify(1,1,n,tr[u].mp,g[u][0],g[u][1]);
if(u==tr[u].top){
Matrix tmp=t.query(1,1,n,tr[u].mp,tr[tr[u].bot].mp);
f[u][0]=min(tmp.f[0][0],tmp.f[0][1]),f[u][1]=min(tmp.f[1][0],tmp.f[1][1]);
if(tr[u].fa) g[tr[u].fa][0]+=f[u][1],g[tr[u].fa][1]+=min(f[u][0],f[u][1]);
}
}
for(int i=1;i<=m;++i){
int a,x,b,y;scanf("%d%d%d%d",&a,&x,&b,&y);
int tmp1=p[a][x^1],tmp2=p[b][y^1];
change(a,x^1,INF),change(b,y^1,INF);
long long res=min(f[1][0],f[1][1]);
if(res>=INF) printf("-1\n");else printf("%lld\n",res);
change(a,x^1,tmp1),change(b,y^1,tmp2);
}
return 0;
}