1. 程式人生 > 其它 >P3647 [APIO2014]連珠線 題解

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;
}