HDU 6567 Cotree(2019CCPC-江西省赛 A)

Problem Description

Avin has two trees which are not connected. He asks you to add an edge between them to make them connected while minimizing the function ∑ni=1∑nj=i+1dis(i,j), where dis(i,j) represents the number of edges of the path from i to j. He is happy with only the function value.

Input

The first line contains a number n (2<=n<=100000). In each of the following n−2 lines, there are two numbers u and v, meaning that there is an edge between u and v. The input is guaranteed to contain exactly two trees.

Output

Just print the minimum function value.

Sample Input

3 1 2

Sample Output

4

给定两棵树,n个节点,n-2条边。现在需要在两棵树中连一条边并令式子的值最小。

首先我们要先知道一个概念,树的重心。对于这个题,树中边的权值设为1即可。

树的重心的性质:

树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。

那么这个题我们要做的就是寻找到两棵树的重心,将重心连起来,然后算距离和。算距离和的时候,我们可以单独考虑每条边对距离和的贡献,因为有一个公式可以方便的求距离和

任意一条边对距离和的贡献 = 左端结点个数 * 右端结点个数 * 边权

这样,新加边的贡献便是一个固定值了。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int n;
ll fa[maxn],si[maxn],f[maxn],g[maxn];
vector< vector<int> >ve;
void dfs(int x)
{
	si[x]=1;//至少一个点,即它本身 
	f[x]=0;//初始化贡献为0 
	for(auto i:ve[x])//遍历与它联通的点 
		if(i != fa[x])//若该节点是它的子节点 
		{
			fa[i]=x;//记录该节点的父节点 
			dfs(i); 
			si[x]+=si[i];//将子树的节点个数加上 
			f[x]+=f[i]+si[i]*(n-si[i]);//加上子树的贡献和这条边的贡献
		}
}
ll dfs(int x,int root,ll num)
{
	if(x != root)//若不是根节点,计算非子树边的贡献 
		g[x]=g[fa[x]]+f[fa[x]]-f[x]+(n-si[x]-num)*(si[x]+num)-si[x]*(n-si[x]);
	else g[x]=0;//为根节点,没有非子树边 
	ll ans=g[x]+f[x];//子树边+非子树边即为树中所有点到该点的距离和 
	for(auto i:ve[x]) 
		if(i != fa[x]) 
			ans=min(ans,dfs(i,root,num));
	return ans;
}
int main()
{
	scanf("%d",&n);//输入
	ve.resize(n+1); 
	for(int i=0;i<=n;i++)//初始化父亲节点为-1 
		fa[i]=-1;
	for(int i=1,x,y;i<n-1;i++)//输入 
	{
		scanf("%d%d",&x,&y);
		ve[x].push_back(y); 
		ve[y].push_back(x);
	}
	int root2,root1=1;//两棵树的根节点 
	fa[1]=1;//令1号节点为根节点 
	dfs(1);//搜索1号节点所在的树 
	ll sonNum=si[1];//第一棵树的节点个数 
	ll fSonNum=n-sonNum;//另一棵树的节点个数 
	for(int i=1;i<=n;i++)//寻找第二棵树的节点 
	{
		if(fa[i] == -1)//找到了第二棵树的节点 
		{
			fa[i]=i;//令该节点为此树的根节点 
			root2=i;//记录该树的根节点 
			dfs(i);
			break;
		}
	}
	cout<<dfs(1,1,fSonNum)+dfs(root2,root2,sonNum)+sonNum*fSonNum<<endl;
} 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值