【树形DP】ZOJ - 3949 Edge to the Root

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;
}

猜你喜欢

转载自blog.csdn.net/bbbbswbq/article/details/79971308