ybtAu「动态规划」第6章 动态 DP

这是 neatisaac 的金牌导航题解!

A. 【例题1】最大子段和

DDP 板子。
\(f_i\) 表示以 \(i\) 结尾的最大子段和,\(g_i\) 表示前 \(i\) 个数的最大子段和,有:

\[\large f_i=\max(f_{i-1}+a_i,a_i) \\\large g_i=\max(g_{i-1},f_i) \]

放进矩阵,有:

\[\large \left[ \begin{array}{l} f_i\\g_i\\0 \end{array} \right] =\left[ \begin{array}{l} a_i&-\infin&a_i\\ a_i&0&a_i\\ -\infin&-\infin&0 \end{array} \right] \left[ \begin{array}{l} f_{i-1}\\g_{i-1}\\0 \end{array} \right] \]

线段树维护即可。

#include <iostream>
#include <cstring>
#define int long long
#define N 500005
int n,m,a[N],rt;
struct Mat
{
	int o[3][3];
	Mat() {memset(o,-0x3f,sizeof o);}
	Mat(int x)
	{
		o[0][0]=o[0][2]=o[1][0]=o[1][2]=x;
		o[1][1]=o[2][2]=0;
		o[0][1]=o[2][0]=o[2][1]=-1e18;
	}
	Mat operator *(const Mat &g) const
	{
		Mat ret;
		for(int k=0;k<3;k++) for(int i=0;i<3;i++) for(int j=0;j<3;j++)
			ret.o[i][j]=std::max(ret.o[i][j],o[i][k]+g.o[k][j]);
		return ret;
	}
};
namespace SGT
{
	Mat d[N<<1];
	int ls[N<<1],rs[N<<1],idx;
	#define mid (lb+rb>>1)
	int build(int lb,int rb)
	{
		int x=++idx;
		if(lb==rb) return d[x]=a[lb],x;
		ls[x]=build(lb,mid),rs[x]=build(mid+1,rb),d[x]=d[ls[x]]*d[rs[x]];
		return x;
	}
	void md(int x,int t,int k,int lb,int rb)
	{
		if(lb==rb) d[x]=k;
		else ((t<=mid)?md(ls[x],t,k,lb,mid):md(rs[x],t,k,mid+1,rb)),d[x]=d[ls[x]]*d[rs[x]];
	}
	Mat qr(int x,int l,int r,int lb,int rb)
	{
		if(l<=lb&&rb<=r) return d[x];
		if(r<=mid) return qr(ls[x],l,r,lb,mid);
		if(l>mid) return qr(rs[x],l,r,mid+1,rb);
		return qr(ls[x],l,r,lb,mid)*qr(rs[x],l,r,mid+1,rb);
	}
	#undef mid
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n>>m;
	for(int i=1;i<=n;i++) std::cin>>a[i];
	rt=SGT::build(1,n);
	for(int i=1,op,x,y;i<=m;i++)
	{
		std::cin>>op>>x>>y;
		if(op==1)
		{
			if(x>y) std::swap(x,y);
			Mat ans=SGT::qr(rt,x,y,1,n);
			std::cout<<std::max(ans.o[1][0],ans.o[1][2])<<'\n';
		}
		if(op==2) SGT::md(rt,x,y,1,n);
	}
}

B. 【例题2】最大独立集

树上 DDP 板子,也是 DDP 更主要的应用。
但是懒得打式子了。

#include <iostream>
#include <cstring>
#define N 200005
int n,m,a[N],hed[N],tal[N],nxt[N],cnte,f[N][2],g[N][2],rt,li[N],bot[N];
void adde(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
struct Mat
{
	int o[2][2];
	Mat() {memset(o,-0x3f,sizeof o);}
	Mat operator *(const Mat &g) const
	{
		Mat ret;
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++)
			ret.o[i][j]=std::max(ret.o[i][j],o[i][k]+g.o[k][j]);
		return ret;
	}
} b[N];
namespace SGT
{
	Mat d[N<<1];
	int ls[N<<1],rs[N<<1],idx;
	#define mid (lb+rb>>1)

	int build(int lb,int rb)
	{
		int x=++idx;
		if(lb==rb) return d[x]=b[li[lb]],x;
		return ls[x]=build(lb,mid),rs[x]=build(mid+1,rb),d[x]=d[ls[x]]*d[rs[x]],x;
	}
	void md(int x,int t,int lb,int rb)
	{
		if(lb==rb) d[x]=b[li[lb]];
		else (t<=mid)?md(ls[x],t,lb,mid):md(rs[x],t,mid+1,rb),d[x]=d[ls[x]]*d[rs[x]];
	}
	Mat qr(int x,int l,int r,int lb,int rb)
	{
		if(l<=lb&&rb<=r) return d[x];
		if(r<=mid) return qr(ls[x],l,r,lb,mid);
		if(l>mid) return qr(rs[x],l,r,mid+1,rb);
		return qr(ls[x],l,r,lb,mid)*qr(rs[x],l,r,mid+1,rb);
	}
	#undef mid
};
namespace HLD
{
	int dep[N],fa[N],son[N],dfn[N],top[N],siz[N],idx;
	void dfs1(int x)
	{
		siz[x]=1;
		for(int i=hed[x];i;i=nxt[i]) if(!siz[tal[i]])
		{
			dep[tal[i]]=dep[x]+1,fa[tal[i]]=x,dfs1(tal[i]),siz[x]+=siz[tal[i]];
			if(siz[tal[i]]>siz[son[x]]) son[x]=tal[i];
		}
	}
	void dfs2(int x,int tp)
	{
		li[dfn[x]=++idx]=x,top[x]=tp,bot[tp]=std::max(bot[tp],idx);
		f[x][0]=0,f[x][1]=a[x],b[x].o[0][0]=0,b[x].o[1][0]=a[x];
		if(son[x]) dfs2(son[x],tp),f[x][0]+=std::max(f[son[x]][0],f[son[x]][1]),f[x][1]+=f[son[x]][0];
		for(int i=hed[x];i;i=nxt[i]) if(!top[tal[i]])
		{
			dfs2(tal[i],tal[i]),f[x][0]+=std::max(f[tal[i]][0],f[tal[i]][1]),f[x][1]+=f[tal[i]][0];
			b[x].o[0][0]+=std::max(f[tal[i]][0],f[tal[i]][1]),b[x].o[1][0]+=f[tal[i]][0];
		}
		b[x].o[0][1]=b[x].o[0][0];
	}
	void md(int x,int k)
	{
		b[x].o[1][0]+=k-a[x];
		a[x]=k;
		Mat la,nw;
		while(x)
		{
			la=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			SGT::md(rt,dfn[x],1,n);
			nw=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			x=fa[top[x]];
			b[x].o[0][0]+=std::max(nw.o[0][0],nw.o[1][0])-std::max(la.o[0][0],la.o[1][0]);
			b[x].o[1][0]+=nw.o[0][0]-la.o[0][0],b[x].o[0][1]=b[x].o[0][0];
		}
	}
};
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	memset(f,-0x3f,sizeof f);
	std::cin>>n>>m;
	for(int i=1;i<=n;i++) std::cin>>a[i];
	for(int i=1,u,v;i<n;i++) std::cin>>u>>v,adde(u,v),adde(v,u);
	HLD::dfs1(1),HLD::dfs2(1,1),rt=SGT::build(1,n);
	for(int i=1,x,y;i<=m;i++)
	{
		std::cin>>x>>y;
		HLD::md(x,y);
		Mat tmp=SGT::qr(rt,1,bot[1],1,n);
		std::cout<<std::max(tmp.o[0][0],tmp.o[1][0])<<'\n';
	}
}

C. 保卫王国

把必须驻扎军队的城市设为 \(-\infin\),不得驻扎军队的城市设为 \(+\infin\)

#include <iostream>
#include <cstring>
#define int long long
#define N 200005
int n,m,p[N],f[N][2],hed[N],tal[N],nxt[N],cnte,li[N],bot[N],rt,sum;
void adde(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
struct Mat
{
	int o[2][2];
	Mat() {memset(o,0,sizeof o);}
	Mat operator *(const Mat &g) const
	{
		Mat ret;
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) ret.o[i][j]=std::max(ret.o[i][j],o[i][k]+g.o[k][j]);
		return ret;
	}
} a[N];
namespace SGT
{
	Mat d[N<<1];
	int ls[N<<1],rs[N<<1],idx;
	#define mid (lb+rb>>1)
	int build(int lb,int rb)
	{
		int x=++idx;
		if(lb==rb) return d[x]=a[li[lb]],x;
		return ls[x]=build(lb,mid),rs[x]=build(mid+1,rb),d[x]=d[ls[x]]*d[rs[x]],x;
	}
	void md(int x,int t,int lb,int rb)
	{
		if(lb==rb) d[x]=a[li[lb]];
		else (t<=mid)?md(ls[x],t,lb,mid):md(rs[x],t,mid+1,rb),d[x]=d[ls[x]]*d[rs[x]];
	}
	Mat qr(int x,int l,int r,int lb,int rb)
	{
		if(l<=lb&&rb<=r) return d[x];
		if(r<=mid) return qr(ls[x],l,r,lb,mid);
		if(l>mid) return qr(rs[x],l,r,mid+1,rb);
		return qr(ls[x],l,r,lb,mid)*qr(rs[x],l,r,mid+1,rb);
	}
	#undef mid
};
namespace HLD
{
	int dfn[N],fa[N],son[N],siz[N],top[N],idx;
	void dfs1(int x)
	{
		siz[x]=1,f[x][1]=p[x];
		for(int i=hed[x];i;i=nxt[i]) if(!siz[tal[i]])
		{
			fa[tal[i]]=x,dfs1(tal[i]),siz[x]+=siz[tal[i]];
			if(siz[tal[i]]>siz[son[x]]) son[x]=tal[i];
			f[x][0]+=std::max(f[tal[i]][0],f[tal[i]][1]);
			f[x][1]+=f[tal[i]][0];
		}
	}
	void dfs2(int x,int tp)
	{
		top[li[dfn[x]=++idx]=x]=tp,bot[tp]=std::max(bot[tp],idx);
		a[x].o[1][0]=p[x],a[x].o[1][1]=-1e12;
		if(!son[x]) return;
		dfs2(son[x],tp);
		for(int i=hed[x];i;i=nxt[i]) if(!top[tal[i]])
		{
			dfs2(tal[i],tal[i]);
			a[x].o[0][0]+=std::max(f[tal[i]][0],f[tal[i]][1]);
			a[x].o[1][0]+=f[tal[i]][0];
		}
		a[x].o[0][1]=a[x].o[0][0];
	}
	void md(int x,int k)
	{
		a[x].o[1][0]+=k-p[x],p[x]=k;
		Mat la,nw;
		while(x)
		{
			la=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			SGT::md(rt,dfn[x],1,n);
			nw=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			x=fa[top[x]];
			a[x].o[0][0]+=std::max(nw.o[0][0],nw.o[1][0])-std::max(la.o[0][0],la.o[1][0]);
			a[x].o[1][0]+=nw.o[0][0]-la.o[0][0],a[x].o[0][1]=a[x].o[0][0];
		}
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::string tmp1;
	std::cin>>n>>m>>tmp1;
	for(int i=1;i<=n;i++) std::cin>>p[i],sum+=p[i];
	for(int i=1,u,v;i<n;i++) std::cin>>u>>v,adde(u,v),adde(v,u);
	HLD::dfs1(1),HLD::dfs2(1,1),rt=SGT::build(1,n);
	for(int i=1,u,x,v,y;i<=m;i++)
	{
		std::cin>>u>>x>>v>>y;
		if((u==HLD::fa[v]||v==HLD::fa[u])&&!x&&!y) {std::cout<<"-1\n";continue;}
		int tu=p[u],tv=p[v];
		if(x==0) HLD::md(u,1e12);
		if(x==1) HLD::md(u,-1e12);
		if(y==0) HLD::md(v,1e12);
		if(y==1) HLD::md(v,-1e12);
		Mat mt=SGT::qr(rt,1,bot[1],1,n);
		int ans=std::max(mt.o[0][0],mt.o[1][0]);
		if(x==0) ans=ans-1e12+tu;
		if(y==0) ans=ans-1e12+tv;
		std::cout<<sum-ans<<'\n';
		HLD::md(u,tu),HLD::md(v,tv);
	}
}

D. 洪水

\(g_i\) 表示所有轻子树的答案,有:

\[\large f_i=\max(w_i,g_i+f_{son_i}) \\\large g_i=\sum g_v(v\neq son_i) \]

建立矩阵即可。

#include <iostream>
#include <cstring>
#define int long long
#define N 400005
int n,m,w[N],hed[N],tal[N],nxt[N],cnte,rt,li[N],bot[N],f[N];
void adde(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
struct Mat
{
	int o[2][2];
	Mat() {memset(o,0x3f,sizeof o);}
	Mat operator *(const Mat &g) const
	{
		Mat ret;
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++)
			ret.o[i][j]=std::min(ret.o[i][j],o[i][k]+g.o[k][j]);
		return ret;
	}
} a[N];
namespace SGT
{
	Mat d[N<<1];
	int ls[N<<1],rs[N<<1],idx;
	#define mid (lb+rb>>1)
	int build(int lb,int rb)
	{
		int x=++idx;
		if(lb==rb) return d[x]=a[li[lb]],x;
		return ls[x]=build(lb,mid),rs[x]=build(mid+1,rb),d[x]=d[ls[x]]*d[rs[x]],x;
	}
	void md(int x,int t,int lb,int rb)
	{
		if(lb==rb) d[x]=a[li[lb]];
		else (t<=mid)?md(ls[x],t,lb,mid):md(rs[x],t,mid+1,rb),d[x]=d[ls[x]]*d[rs[x]];
	}
	Mat qr(int x,int l,int r,int lb,int rb)
	{
		if(l<=lb&&rb<=r) return d[x];
		if(r<=mid) return qr(ls[x],l,r,lb,mid);
		if(l>mid) return qr(rs[x],l,r,mid+1,rb);
		return qr(ls[x],l,r,lb,mid)*qr(rs[x],l,r,mid+1,rb);
	}
	#undef mid
};
namespace HLD
{
	int fa[N],son[N],siz[N],dfn[N],top[N],idx;
	void dfs1(int x)
	{
		siz[x]=1,f[x]=0;
		for(int i=hed[x];i;i=nxt[i]) if(!siz[tal[i]])
		{
			fa[tal[i]]=x,dfs1(tal[i]),siz[x]+=siz[tal[i]],f[x]+=f[tal[i]];
			if(siz[tal[i]]>siz[son[x]]) son[x]=tal[i];
		}
		a[x].o[0][0]=f[x]-f[son[x]],a[x].o[0][1]=w[x];
		if(!son[x]) a[x].o[0][0]=1e18;
		f[x]=son[x]?std::min(f[x],w[x]):w[x];
		a[x].o[1][0]=a[x].o[1][1]=0;
	}
	void dfs2(int x,int tp)
	{
		if(!x) return;
		top[li[dfn[x]=++idx]=x]=tp,bot[tp]=idx,dfs2(son[x],tp);
		for(int i=hed[x];i;i=nxt[i]) if(!top[tal[i]]) dfs2(tal[i],tal[i]);
	}
	void md(int x,int k)
	{
		a[x].o[0][1]+=k,w[x]+=k;
		while(x)
		{
			Mat la=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			SGT::md(rt,dfn[x],1,n);
			Mat nw=SGT::qr(rt,dfn[top[x]],bot[top[x]],1,n);
			x=fa[top[x]],a[x].o[0][0]+=std::min(nw.o[0][0],nw.o[0][1])-std::min(la.o[0][0],la.o[0][1]);
		}
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n;
	for(int i=1;i<=n;i++) std::cin>>w[i];
	for(int i=1,u,v;i<n;i++) std::cin>>u>>v,adde(u,v),adde(v,u);
	HLD::dfs1(1),HLD::dfs2(1,1),rt=SGT::build(1,n);
	std::cin>>m;
	for(int i=1,x,y;i<=m;i++)
	{
		char op;
		std::cin>>op>>x;
		if(op=='Q')
		{
			Mat mt=SGT::qr(rt,HLD::dfn[x],bot[HLD::top[x]],1,n);
			std::cout<<std::min(mt.o[0][0],mt.o[0][1])<<'\n';
		}
		if(op=='C') std::cin>>y,HLD::md(x,y);
	}
}

E. 切树游戏

未完待续……

posted @ 2025-05-13 09:15  整齐的艾萨克  阅读(8)  评论(0)    收藏  举报