ZOJ 3949 (树形DP)

Time Limit: 1000MS Memory Limit: 131072KB 64bit IO Format: %lld & %llu

 Status

Description

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++.

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).

Sample Input

2
6
1 2
2 3
3 4
3 5
3 6
3
1 2
2 3

Sample Output

8
2
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.


题意:一颗树根节点为1,每直接相连的两个点距离为1,现在可以让1节点和其他任意节点相连一条边,问加入那条边后 从1到其他所有点距离和最小。


分析: 设ans数组是加入1到 i 这条边之后的最小和,sum数组是以i节点为根的子树的总节点数。 dep数组是深度为 i 的节点是谁。

所以可以找出一个递推式。当 i 的子节点是 j 时。

满足:

ans[j]=ans[i]+sum[dep[d/2+1]]-2*sum[j];


代码;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <queue>
#define mem(p,k) memset(p,k,sizeof(p));
#define rep(a,b,c) for(int a=b;a<c;a++)
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x6fffffff
#define ll long long
using namespace std;
int m,n,minn,maxx,x,y,k,cur;
ll dis[210000],sum[210000];
struct edg{
    int to,next;
}edge[410000];
ll head[210000],bk[210000],cnt,dep[210000],ans[210000];
void add(int f,int t){
    edge[cnt].to=t;
    edge[cnt].next=head[f];
    head[f]=cnt++;
}

void build(int f){
    struct edg p;
    for(int i=head[f];~i;i=edge[i].next){
            p=edge[i];
            if(!bk[p.to]){
                dis[p.to]=dis[f]+1;
                bk[p.to]=1;
                build(p.to);
                sum[f]+=sum[p.to];
            }
    }
    return ;
}

void dfs(int f,int d){
    struct edg p;
    for(int i=head[f];~i;i=edge[i].next){
            p=edge[i];
            if(!bk[p.to]){
                bk[p.to]=1;
                dep[d]=p.to;
                if(f!=1)ans[p.to]=ans[f]+sum[dep[d/2+1]]-2*sum[p.to];
                else ans[p.to]=ans[1];
                dfs(p.to,d+1);
            }
    }
}

void solve(){
    mem(head,-1);
    for(int i=0;i<n-1;i++){
        int s,e;
        scanf("%d%d",&s,&e);
        add(s,e);
        add(e,s);
    }
    for(int i=1;i<=n;i++)sum[i]=1,bk[i]=0;
    dis[1]=0;
    bk[1]=1;
    build(1);

    ans[1]=0;
    for(int i=2;i<=n;i++)ans[1]+=dis[i];
    //cout<<ans[1]<<endl;
    mem(bk,0);
    bk[1]=1;
    dep[0]=1;
    dfs(1,1);

    ll maxx=1e15;
    for(int i=1;i<=n;i++){
        //cout<<i<<"=="<<ans[i]<<endl;
        if(maxx>ans[i]){
            maxx=ans[i];
        }
    }

    printf("%lld\n",maxx);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        cnt=0;
        scanf("%d",&n);
        solve();
    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值