动态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;
}
posted @ 2021-12-13 16:18  Point_King  阅读(38)  评论(0编辑  收藏  举报