CF1187E Tree Painting 題解
阿新 • • 發佈:2020-08-03
CF1187E Tree Painting 題解
原題面
前置知識: 換根\(DP\)
換根\(DP\)模板題
如果您還不會換根\(DP\)的話,可以先去看看UM巨佬的日報:
大致題意
給定一棵n個點的樹 初始全是白點
要求你做n步操作,每一次選定一個與一個黑點相隔一條邊的白點,將它染成黑點,然後獲得該白點被染色前所在的白色聯通塊大小的權值。
求可獲得的最大權值
分析
幾乎是一道裸的模板題了...
和P3478幾乎一摸一樣,只是需要一個微小的結論
PS:圖中節點的編號有一點微小的錯誤,不過並不影響閱讀
假如說我們選了圖中的1號節點作為第一個塗色的點(圖中藍色的點)
那下一個塗色的節點肯定就能選擇它的幾個兒子了(圖中深紅色的點)
同時,由於父親節點已經被塗色了,其子節點不可能再和上面的"祖先"輩節點有聯通了
能對其產生貢獻的只有自己的子樹
因此當一個父親節點被塗色後,其所有子樹都是相對"獨立"的,塗色順序的變化對總貢獻值無任何影響
故當第一個節點被塗色後,剩下節點的塗色順序均無法對總貢獻值產生影響
程式碼實現
單純的暴力列舉每個根的位置的話照這個資料範圍肯定會T飛
考慮換根DP
應該很容易狀態轉移方程推出:
- \(dp[v] = dp[u]-2size[v]+size[1]\)
具體這個方程怎麼來的,我之前寫的P3478的 題解跟前面UM巨佬的日報裡也有講
套上換根\(DP\)的板子即可
貼上醜陋的程式碼:(其實只要把P3478的程式碼改一行就可以了)
#include<bits/stdc++.h> using namespace std; const int MAXN = 200010; vector <int> son[MAXN]; int vis[MAXN],n; long long size[MAXN]; long long f[MAXN]; void dfs(int u){ size[u] = 1; vis[u] = 1; for(int i=0;i<son[u].size();i++){ int v = son[u][i]; if(!vis[v]){ dfs(v); size[u]+=size[v]; } } } void dp(int u){ vis[u] = 1; for(int i=0;i<son[u].size();i++){ int v = son[u][i]; if(!vis[v]){ f[v] = f[u] + size[1] - 2*size[v]; dp(v); } } } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); son[u].push_back(v); son[v].push_back(u); } dfs(1); for(int i=1;i<=n;i++){ f[1]+=size[i]; } memset(vis,0,sizeof(vis)); dp(1); long long ans = -0x3f; for(int i=1;i<=n;i++){ ans = max(ans , f[i]); } printf("%lld",ans); }