Edge to the Root ZOJ - 3949 (树形DP)

Given a tree with n vertices, we want to add an edge between vertex 1 and vertex x, so that the sum of d(1, v) for all vertices v in the tree is minimized, where d(uv) is the minimum number of edges needed to pass from vertex u to vertex v. Do you know which vertex x we should choose?

Recall that a tree is an undirected connected graph with n vertices and n - 1 edges.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1 ≤ n ≤ 2 × 105), indicating the number of vertices in the tree.

Each of the following n - 1 lines contains two integers u and v (1 ≤ uv ≤ n), indicating that there is an edge between vertex u and v in the tree.

It is guaranteed that the given graph is a tree, and the sum of n over all test cases does not exceed 5 × 105. As the stack space of the online judge system is not very large, the maximum depth of the input tree is limited to about 3 × 104.

We kindly remind you that this problem contains large I/O file, so it's recommended to use a faster I/O method. For example, you can use scanf/printf instead of cin/cout in C++.

<h4< dd="">Output

For each test case, output a single integer indicating the minimum sum of d(1, v) for all vertices v in the tree (NOT the vertex x you choose).

<h4< dd="">Sample Input
2
6
1 2
2 3
3 4
3 5
3 6
3
1 2
2 3
<h4< dd="">Sample Output
8
2

<h4< dd="">

Hint

For the first test case, if we choose x = 3, we will have

d(1, 1) + d(1, 2) + d(1, 3) + d(1, 4) + d(1, 5) + d(1, 6) = 0 + 1 + 1 + 2 + 2 + 2 = 8

It's easy to prove that this is the smallest sum we can achieve.

题意:首先给你n个点, 然后给你n -  1条边, 这n - 1条边形成一棵树, 定义d(1, v)表示1这个点到v这个点之间的距离, 之后你有一个操作, 可以额外多连一条边, 这条边可以连在1于任意一个点上, 之后问d(1, 2) + d(1, 3) + ........ + d(1, n), 并让其最小;

思路:看到这个之后很本能的反应就是每个点都与1加一条边, 然后取个最小值嘛, 但是会不会复杂度高呢? 不然, 这里有个很重要的点就是按深度来更新, 从当前点更新这个点的下一个节点, 那么只会影响到当前点深度的一半处的点, 这个画一下图就可以看出来, 之后我们只需要记录每一个深度有多少个点, 每一个点有多少个子节点, 这样我们就可以按深度自顶向下进行更新了, 具体看代码就好了;

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxed = 200000 + 10;
typedef long long ll;
struct E
{
    int v, bef;
}e[maxed * 2];
int n, ans, head[maxed];
ll dp[maxed], size_[maxed], d[maxed];
int main()
{
    void add_(int x, int y);
    void slove_1(int u, int fa);
    void slove_2(int u, int fa, int dep);
    int N;
    scanf("%d", &N);
    while (N--) {
        ans = 1;
        memset(head, -1, sizeof(head));
        scanf("%d", &n);
        int a, b;
        for (int i = 1; i <= n - 1; ++i) {
            scanf("%d%d", &a, &b);
            add_(a, b);
            add_(b, a);
        }
        dp[1] = 0;
        slove_1(1, 1);
        slove_2(1, 1, 0);
        ll answer = dp[1];
        for (int i = 2; i <= n; ++i)
            answer = min(answer, dp[i]);
        printf("%lld\n", answer);
    }
    return 0;
}
void add_(int x, int y)
{
    e[ans].v = y;
    e[ans].bef = head[x];
    head[x] = ans++;
}
void slove_1(int u, int fa)
{
    size_[u] = 1;
    for (int i = head[u]; i != -1; i = e[i].bef) {
        int v = e[i].v;
        if (v == fa)
            continue;
        slove_1(v, u);
        size_[u] += size_[v];
        dp[1] += size_[v];
    }
}
void slove_2(int u, int fa, int dep)
{
    d[dep] = size_[u];
    for (int i = head[u]; i != -1; i = e[i].bef) {
        int v = e[i].v;
        if (v == fa)
            continue;
        d[dep + 1] = size_[v];
        if (u == 1)
            dp[v] = dp[u];
        else {
            if (dep % 2)
                dp[v] = dp[u] + d[(dep + 1) / 2 + 1] - 2 * size_[v];
            else
                dp[v] = dp[u] + d[dep / 2 + 1] - 2 * size_[v];
        }
        slove_2(v, u, dep + 1);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值