cunzai_zsy0531

关注我

P3647 [APIO2014]连珠线 题解

题面

首先可以注意到一点:如果这棵树确定了根节点,那么所有的蓝边都只能是爷爷-父亲-儿子这样的边,不可能有儿子-父亲-另一个儿子这样的(建不出来)。

基于上述结论的一个 \(O(n^2)\) dp就是:枚举根节点,设 \(f_{u,0/1}\) 表示 \(u\) 节点子树中,\(u\) 是否作为某一条蓝边链中点的最大蓝边长度。转移方程说的是:

\[f_{u,0}=\sum_{v\in son_u}\max(f_{v,0},f_{v,1}+w(u,v)) \]

\[f_{u,1}=f_{u,0}+\max_{v\in son_u}\{f_{v,0}+w(u,v)-\max(f_{v,0},f_{v,1}+w(u,v))\} \]

现在考虑使用换根dp来优化。首先设 \(dp_{u,0/1,v}\) 表示 \(u\) 点不考虑 \(v\) 这个儿子的dp值,由于树上所有点的儿子总数是 \(O(n)\) 的,所以这个状态也是 \(O(n)\) 的。注意在计算 \(f_{u,1}\) 的时候,是首先记录了每个儿子信息的 \(\max\)。由于需要计算删掉一个儿子的dp值,有可能会删掉取到 \(\max\) 的那个儿子,所以在这里需要额外记录一个次大值。

考虑换根dp的过程:走到 \(v\) 点的时候,\(g_{u,0/1}\) 应该表示的是 \(u\)\(v\) 以外的儿子以及父亲合并起来的dp值,这样在计算以 \(v\) 为根的答案时,只需要将它的父亲 \(u\) 也作为一个儿子合并dp值即可。这个方程不难转移(和上边是差不多的),然后就做完了。

注意换根dp的边界有些时候可能会出错,比如这个题需要尤其注意 \(fa=0\) 时的情况。

点击查看代码
#include<iostream>
#include<cstdio>
#include<vector>
#define pb push_back
#define mp std::make_pair
#define fi first
#define se second
typedef std::pair<int,int> pii;
inline int max(const int &a,const int &b){return a>b?a:b;}
inline int rd(){
	int res=0;char c=getchar();
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())res=(res<<1)+(res<<3)+(c-'0');
	return res;
}
const int N=2e5+13,INF=0x3f3f3f3f;
int n,f[N][2],g[N][2],ans,a[N];
std::vector<int> dp[N][2];
std::vector<pii> e[N];
inline void add_edge(int u,int v,int w){e[u].pb(mp(v,w));}
void dfs1(int u,int fa){
	f[u][1]=-INF;
	int mx1=-INF,mx2=-INF,son1=0;
	for(int i=0,lim=e[u].size();i<lim;++i){
		int v=e[u][i].fi,w=e[u][i].se;if(v==fa) continue;
		dfs1(v,u);a[v]=w;
		f[u][0]+=max(f[v][0],f[v][1]+w);
		int tmp=f[v][0]+w-max(f[v][0],f[v][1]+w);
		if(tmp>mx1) mx2=mx1,mx1=tmp,son1=v;
		else if(tmp>mx2) mx2=tmp;
	}
	if(!son1) return;
	f[u][1]=f[u][0]+mx1;
	for(int i=0,lim=e[u].size();i<lim;++i){
		int v=e[u][i].fi,w=e[u][i].se;
		if(v==fa){dp[u][0].pb(0),dp[u][1].pb(0);continue;}
		dp[u][0].pb(f[u][0]-max(f[v][0],f[v][1]+w));
		if(v!=son1) dp[u][1].pb(f[u][1]-max(f[v][0],f[v][1]+w));
		else dp[u][1].pb(f[u][0]-max(f[v][0],f[v][1]+w)+mx2);
	}
}
void dfs2(int u,int fa){
	ans=max(ans,f[u][0]+max(g[fa][0],g[fa][1]+a[u]));
	for(int i=0,lim=e[u].size();i<lim;++i){
		int v=e[u][i].fi;if(v==fa) continue;
		int mx=dp[u][1][i]-dp[u][0][i];
		if(fa){
			g[u][0]=dp[u][0][i]+max(g[fa][0],g[fa][1]+a[u]);
			g[u][1]=g[u][0]+max(mx,g[fa][0]+a[u]-max(g[fa][0],g[fa][1]+a[u]));	
		}
		else g[u][0]=dp[u][0][i],g[u][1]=dp[u][1][i];
		dfs2(v,u);
	}
}
int main(){
	n=rd();
	for(int i=1;i<n;++i){
		int u=rd(),v=rd(),w=rd();
		add_edge(u,v,w),add_edge(v,u,w);
	}
	dfs1(1,0);
	dfs2(1,0);
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-05-20 11:58  cunzai_zsy0531  阅读(24)  评论(0编辑  收藏  举报