Time Limit: 1000MS | Memory Limit: 131072KB | 64bit IO Format: %lld & %llu |
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(u, v) 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 ≤ u, v ≤ 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;
}