P3478 [POI2008]STA-Station題解
阿新 • • 發佈:2020-08-03
P3478 [POI2008]STA-Station題解
原題面
知識點
- 換根DP
大致題意
給出一個 N 個點的樹,找出一個點來,以這個點為根的樹時,所有點的深度之和最大
分析
換根DP的模板題。
如果您還不會換根DP的話,可以先去看看UltiMadow巨佬的文章:
【日報#278】[學習筆記]換根dp,我一開始也是從那裡學的qwq
這裡我們設
-
\(size[i]\)為以\(1\)為根節點時節點\(i\)的子樹大小
-
\(dep[i]\)為以\(1\)為根節點時節點\(i\)的深度大小
-
\(dp[i]\)為以\(i\)為根節點時深度之和的大小
很明顯,我們可以通過一遍DFS求出以\(1\)
如果一個個的去算的話
照這個資料範圍,顯然會T飛
這個時候就要用到換根DP了
換根\(DP\)優化
可以看出,當我們把根節點從1換到3時
對子節點3的貢獻由兩部分組成
1.自己子樹的貢獻(圖中的k)
2.父親節點\(1\)的貢獻
如何轉移
-
首先是\(k\),作為自己子樹所產生的貢獻肯定要加上
-
\(dp[u]\)為以\(u\)為根節點時的深度總值,在計算時,要減去\(v\)的子樹所產生的貢獻,不然就重複計算了,同時
在以 \(u\)為根時,v節點及其子樹內的所有節點的深度都增加了\(1\),需要減去
(圖中紅色的節點)
合起來就是\(dp[u]-(size[v]+k)\)
- 除v子樹外的其他節點也一樣
在以\(v\)為根時,除\(v\)節點及其子樹外的其他節點的深度都增加了\(1\)
(圖中藍色的節點)
合起來就是\((size[1]-size[v])\)
得到轉移方程
- \(dp[v] = k+(dp[u]-(k+size[v]))+(size[1]-size[v])\)
化簡一下
- \(dp[v] = dp[u]-2size[v]+size[1]\)
轉移方程推出來了,程式碼部分就不難實現了,兩遍dfs,一次dfs統計子樹內的節點對當前節點的貢獻
一次dfs換根
貼個程式碼:
#include<bits/stdc++.h> using namespace std; const int MAXN = 100010; long long dp[MAXN],dep[MAXN],size[MAXN]; int vis[MAXN]; vector <int> son[MAXN]; int n; void dfs1(int x){ size[x] = 1; vis[x] = 1; for(int i=0;i<son[x].size();i++){ int v = son[x][i]; if(!vis[v]){ dep[v] = dep[x] +1; dfs1(v); size[x]+=size[v]; } } } void dfs2(int x){ vis[x] = 1; for(int i=0;i<son[x].size();i++){ int v = son[x][i]; if(!vis[v]){ dp[v] = dp[x] +size[1] - 2*size[v]; dfs2(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); } dfs1(1); for(int i=1;i<=n;i++) dp[1]+=dep[i]; memset(vis,0,sizeof(vis)); dfs2(1); long long ans = -0x3f; int jd =999; for(int i=1;i<=n;i++){ if(ans < dp[i]) ans = dp[i], jd = i; } cout<<jd; }