CodeForces - 1324F Maximum White Subtree(樹形dp)

題目鏈接:點擊查看

題目大意:給出 n 個點組成的樹,每個點都有一個顏色,非黑即白,現在問對於每個點而言,選出一個連通塊,使得白色點的個數與黑色點的個數做差最大

題目分析:記錄一下div3的第一次ak,實際上應該是僞ak,感謝zx學長最後擡了我一手F題,dp真的是天克我,不過通過這個題真的學到了不少

首先這個題,題意給出的subtree一定別想當然以爲是子樹,我一開始就是因爲這裏讀錯題然後就被卡懵了,這個題中的subtree其實是子連通塊的意思,知道了子連通塊之後,我們就可以兩次樹形dp來做了,因爲我們要使得 cnt_white - cnt_black 儘可能大,那麼每個節點如果顏色爲白色,其貢獻就是 1 ,否則就是 -1 ,這樣問題就轉換爲了兩次 dp 求出最大子連通塊的權值和,一次是自底向上,一次自頂向下,自底向上的話我們可以直接維護dp[ i ],表示以點 i 爲根的子樹中的最大連通塊,注意這個子樹是有方向的,這也就意味着我們需要自頂向下再來一次,這一次我們主要是爲了處理自底向上過程中無法包含的 fa - u 這條樹鏈所代表的子樹,第二次dfs的時候我們就可以直接維護答案了,這一次我們從上向下走的時候,需要額外維護一個sum變量,用於儲存 fa - u 這條鏈所代表的子樹的貢獻,顯然 sum 最小爲 0,代表的意義就是不選 fa - u 這條鏈,此時的答案也就是 ans[ u ] = dp[ u ] + sum 了,現在的問題轉換爲了如何向下傳遞 sum ,其實無非只有兩種情況:

  1. ans[ u ]大於 0,可以向子樹傳遞貢獻
  2. ans[ u ]小於等於 0,無法向子樹傳遞貢獻

爲什麼要討論ans[ u ]呢,因爲ans[ u ]此時已經完成了自底向上+自頂向下的更新了,也就是說ans[ u ]代表的就是包含 u 在內的子連通塊的最大權值了,所以ans[ u ]的正負,就決定着能否對相鄰的子節點做出貢獻了,如果ans[ u ]爲正的話,那麼減去 dp[ v ] 就是 fa - u 這條鏈代表的子樹的權值了

最後代碼實現起來還是比較好寫的了

代碼:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
      
typedef long long LL;

typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
 
const int N=2e5+100;

int a[N],dp[N],ans[N];

vector<int>node[N];

void dfs1(int u,int fa)//自底向上
{
	dp[u]=a[u];
	for(auto v:node[u])
	{
		if(v==fa)
			continue;
		dfs1(v,u);
		dp[u]+=max(dp[v],0);
	}
}

void dfs2(int u,int fa,int sum)//自頂向下
{
	ans[u]=dp[u]+sum;
	for(auto v:node[u])
	{
		if(v==fa)
			continue;
		dfs2(v,u,max(0,ans[u]-max(0,dp[v])));
	}
}

int main()
{
#ifndef ONLINE_JUDGE
//    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif
//    ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",a+i);
		if(a[i]==0)
			a[i]=-1;
	}
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		node[u].push_back(v);
		node[v].push_back(u);
	}
	dfs1(1,-1);
	dfs2(1,-1,0);
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	
	
	
	
	
	
    
    
    
    
    
    
    
    
    
    
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章