Step1 Problem:
给出一颗根为 1 的树,每条边边长为 1,请你从根连一条边到某个点,使得根到各点距离的总和最小,求这个最小距离。
数据范围:
1<=n<=2e5
Step2 Involving:
树形DP
Step3 Ideas:
假设 1 到 x 连边,受影响的只有 1 和 x 中点往下的点。
例如:一条链长度为 n,给它们按顺序 1 到 n 编号,1 和 n 连边,受影响的只有 (1+n)/2+1 点和往下的点,我们称 (1+n)/2+1 为 1 到 n 的中点。
我们需要求 1 和其他所有点连边的 距离和变化值,然后求变化值最大。
假设 u 是 f 的孩子。
我们从 1 和 u 连边,变化到 1 和 f 连边
f 子树所有结点 距离都减少了 1
1 到 f 的中点子树所有结点 距离都增加了1( f 子树所有结点除外)
那么我们只要预处理出,每个子树的孩子数 siz[]。
1 和 u 连边变化量为 now
1 和 f 连边变化量为 now - siz[st[cnt]] + (siz[st[(cnt+1)/2 + 1]] - siz[st[cnt]]);// st[i] 是 i 编号对应的那个结点
Step4 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5+100;
const int inf = 0x3f3f3f3f;
vector<int> Map[N];
int head[N], cnt;
int siz[N], dep[N], st[N];
ll ans;
void dfs1(int u, int f)//预处理出子树孩子数(包括自身),求出最大距离
{
siz[u] = 1; dep[u] = dep[f] + 1;
ans += dep[u];
for(int i = 0; i < Map[u].size(); i++)
{
int to = Map[u][i];
if(to != f)
{
dfs1(to, u);
siz[u] += siz[to];
}
}
}
ll Min;
void dfs2(int u, int f, ll now)//求出最大变化量
{
st[++cnt] = u;
ll now1 = now;
if(cnt > 2)
{
now1 = now - siz[st[cnt]] + (siz[st[(cnt+1)/2 + 1]] - siz[st[cnt]]);
Min = min(Min, now1);
}
for(int i = 0; i < Map[u].size(); i++)
{
int to = Map[u][i];
if(to != f)
{
dfs2(to, u, now1);
cnt--;
}
}
}
int main()
{
int T, n, u, v;
cin >> T;
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) Map[i].clear();
memset(head, -1, sizeof(head));
cnt = 0;
for(int i = 1; i < n; i++)
{
scanf("%d %d", &u, &v);
Map[u].push_back(v);
Map[v].push_back(u);
}
dep[0] = -1; cnt = ans = Min = 0;
dfs1(1, 0);
dfs2(1, 0, 0);
cout << ans+Min << endl;
}
return 0;
}