CodeForces - 1984E

SmileMask發表於2024-07-11

題目大意

每次在每個聯通塊中選一個點 \(u\),刪除這個點使得聯通塊分成若干個聯通塊,再從聯通塊中選點 \(v\),在新樹上連線 \(u,v\),求新樹葉節點的最大個數。

分析

易轉化為求原樹的最大獨立集,設 \(f_{u,0/1}\) 為以 1 為根時不選/選 \(u\) 的最大獨立集。顯然有:

\[dp_{u,0}=\sum\limits_vmax(dp_{v,0/1})\\ dp_{u,1}=\sum\limits_vdp_{v,0}+1 \]

手模第一個樣例發現如果以原樹的葉子為根節點時,可以同時選擇相鄰的點,要求以各節點為根的最大獨立集,考慮換根 dp,設 \(g_{u,0/1}\) 為以 \(u\) 為根,不選/選 \(u\) 的最大獨立集。顯然有

\[g_{u,0}=max(g_{fa_u,1},f_{u,0}+g_{fa_u,0}-max(f_{u,0},f_{u,1})\\ g_{u,1}=f_{u,1}+g_{fa_u,0}-max(f_{u,0},f_{u,1})\]

程式碼

void dfs(int u,int par){
	f[u][0]=0,f[u][1]=1;
	for(int v:G[u]){
		if(v==par) continue;
		dfs(v,u);
		f[u][0]+=max(f[v][1],f[v][0]);
		f[u][1]+=f[v][0];
	}
}

void DFS(int u,int par){
	ans=max(ans,max(g[u][1],g[u][0]+(G[u].size()==1)));
	for(int v:G[u]){
		if(v==par) 
			continue;
		g[v][0]=max(g[u][1],f[v][0]+(g[u][0]-max(f[v][0],f[v][1])));
		g[v][1]=f[v][1]+(g[u][0]-max(f[v][0],f[v][1]));
		DFS(v,u);
	}
}

void Main(){
	n=rd;
	for(int i=1;i<n;i++){
		int u=rd,v=rd;
		G[u].pb(v),G[v].pb(u);
	}
	dfs(1,0);
	g[1][0]=f[1][0],g[1][1]=f[1][1];
	DFS(1,0);
	cout<<ans<<endl;
	ans=0;
	for(int i=1;i<=n;i++) G[i].clear();
}