引子
说白了就是两次 DFS,第一次 DFS 预处理深度,点权和之类的信息,第二次 DFS 开始运行换根动态规划,题目比较简单的都很板。
P3478 [POI 2008] STA-Station
换根DP模板。
"换根"过程参考图。
原图
1
2
3
4
5
6
假设现在dfs到了1,枚举儿子时找到了2,那么预处理得知dp[1]=11,换根之后示意图。
2
1
3
4
5
6
改变了什么?显然目前根结点在原图上的向上子树距离目前根节点的距离+1,其余结点距离目前根节点的距离-1,那么动态转移方程为dp[v]=dp[x]-sz[v]+(n-sz[v]),也可以写成dp[v]=dp[x]-sz[v]*2+n
cpp
#include<bits/stdc++.h>
using namespace std;
vector<int> E[1000005];
long long n,dp[1000005],dep[1000005],sz[1000005];
// 结点数 DP数组 深度 子树结点数
void dfs(int x,int fa){
dp[1]+=dep[x];//先求出根节点为1的答案
sz[x]=1;//自己算一个
for(int i=0;i<E[x].size();i++){
int v=E[x][i];
if(v==fa)continue;
dep[v]=dep[x]+1;//直接求
dfs(v,x);
sz[x]+=sz[v];//加上儿子的
}
}
void dfs1(int x,int fa){
for(int i=0;i<E[x].size();i++){
int v=E[x][i];//儿子节点
if(v==fa)continue;
dp[v]=dp[x]-sz[v]+(n-sz[v]);//核心代码,其余全是预处理
//计算儿子结点的DP
dfs1(v,x);
}
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
E[u].push_back(v);
E[v].push_back(u);
}
dfs(1,0);
dfs1(1,0);
long long mn=0,m;//看题
for(int i=1;i<=n;i++){
if(dp[i]>mn){
mn=dp[i];
m=i;
}
}
cout<<m;
return 0;
}