Cotree

Cotree

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)∑i=1n∑j=i+1ndis(i,j), where dis(i,j)dis(i,j) represents the number of edges of the path from ii to jj. He is happy with only the function value.

Input

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

Output

Just print the minimum function value.

Sample Input

3
1 2

Sample Output

4

这道题就是找两棵树的重心,然后将两个重心相连得到的最小贡献值。

首先重心的定义:的重心也叫的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。

将两棵树的重心相连,即得到合法的树。

其中一条边的贡献值=左结点数*右结点数*这条边的权值,这里我们默认每条边的权值都为1.

 首先将数的关系存进vector中,选定1为其所在的树的根结点,然后从1结点遍历1所在的整棵树,求出其每条边的贡献值,并记录其结点个数,然后将遍历到的另一棵树的第一个结点作为第二棵树的根,然后分别求出两棵树的每条边的贡献值,再加上两棵树重心相连的边的贡献值,(其中非根节点的边的贡献值的公式并不理解🤔)

以下是ac代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
int n;
ll fa[maxn],si[maxn],f[maxn],g[maxn];//fa:父结点,si:该树结点的个数,f:该结点子树边的贡献值,g:非子树边的贡献值 
vector<int> ve[maxn];

void dfsfind(int x){
	si[x]=1;//至少包含他本身 
	f[x]=0;//初始贡献值为0 
	int i;
	for(auto i:ve[x]){

		if(i!=fa[x]){ 
			fa[i]=x;
			dfsfind(i);
			si[x]+=si[i];
			f[x]+=f[i]+si[i]*(n-si[i]); 
		}
	} 
}

ll dfsvalue(int x,int root,ll sum){
//	cout<<"loop"<<endl;
	if(x!=root){//不是该子树的根结点 
		g[x]=g[fa[x]]+f[fa[x]]-f[x]+(n-si[x]-sum)*(si[x]+sum)-si[x]*(n-si[x]);
	}
	else g[x]=0;//根节点的非子树的贡献值为0 
	ll ans=g[x]+f[x];
	int i;
	for(auto i:ve[x]){
		if(i!=fa[x])
		ans=min(ans,dfsvalue(i,root,sum));
	}
	return ans;
} 

int main(){
//	ios::sync_with_stdio(false);
//	cin.tie(0),cout.tie(0);
	cin>>n;
//	ve.resize(n+1); 
//	cout<<"****";
	for(int i=0;i<=n;i++){
		fa[i]=-1;//初始化父结点 
	}

	for(int i=0;i<(n-2);i++){
		int x,y;
		cin>>x>>y;
		ve[x].push_back(y); 
		ve[y].push_back(x);
	} 
	int root1=1,root2;
	fa[1]=1;//将1结点为1在的树的根 
	dfsfind(1);
	ll sum1=si[1],sum2=n-si[1];//两棵树的结点数 
	for(int i=1;i<=n;i++){
		if(fa[i]==-1){//该结点位于另一棵没有遍历的树 
			fa[i]=i;
			root2=i;
			dfsfind(i);
			break; 
		}
	}	 
	
	cout<<dfsvalue(1,1,sum2)+dfsvalue(root2,root2,sum1)+sum1*sum2<<endl;
	
	return 0;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值