C. Link Cut Centroids(树的重心性质)

题意:

对于一棵树 删除一条边再加上一条边使得新树只有一个重心

输入
输入由多个测试案例组成。第一行包含一个整数t(1≤t≤104)--测试案例的数量。测试用例的描述如下。

每个测试用例的第一行包含一个整数n(3≤n≤105)--顶点的数量。

接下来的n-1行中的每一行都包含两个整数x,y(1≤x,y≤n)。这意味着,存在一条连接顶点x和y的边。

可以保证给定的图是一棵树。

保证所有测试案例的n之和不超过105。

输出
对于每个测试案例,打印两行。

在第一行打印两个整数x1,y1(1≤x1,y1≤n),这意味着你切断了顶点x1和y1之间的边。应该存在连接顶点x1和y1的边。

在第二行打印两个整数x2,y2(1≤x2,y2≤n),这意味着你增加了顶点x2和y2之间的边。

这两次操作后的图形应该是一棵树。

如果有多个解决方案,你可以打印任何一个。

树的重心:只有当你切掉这个顶点(移除它并移除这个顶点的所有边)时,剩余图形的最大连接部分的大小才是最小的。

树的重心的一些性质:
1.删除重心后的所有子树,节点树都不超过原树的一半。
2.一颗树最多两个重心,而且相邻。
3.树中所有的节点到重心的距离之和最小,要是有两个重心,那么他们的距离相等。
4.这颗树要是删除或者增加一条边,重心最多只移动一条边。

题解:

分情况讨论
1.对于树如果是只有一个重心,那么随便取一个与他直连的点,断开然后再连接即可

2.如果有两个重心,根据重心的性质我们可以知道,这两个x,y重心必定相邻,那么我们就取其中一个重心x,找到其他与他直连的点z并且这个点和另一个重心y不相连,然后断开x与z,连接z与y即可

#include<iostream>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
int vis[100050];
vector<int> p[100050];
int son[100060];
int res = 0;
int n;
int t1 = 0,t2 = 0;
int ans = 0;
int dfs(int u)
{
	vis[u] = 1;
	int sum = 1,res = 0;
	for(int i = 0;i < p[u].size();i++)
	{
		int j = p[u][i];
		if(!vis[j])
		{
			int s = dfs(j);
			sum += s; 
			res = max(s,res);
		}
	}
	res = max(res,n - sum);
	if(ans >= res)
	{
		son[u] = res;
		t2 = t1;
		t1 = u;
		ans = res;
	}
	return sum;
}
void solve()
{
	cin >> n;
	for(int i = 1;i <= n;i++)
	p[i].clear(),vis[i] = 0,son[i] = 0;
	res = 0; ans = 1e9;
	t1 = 0,t2 = 0;
	for(int i = 1;i < n;i++)
	{
		int l,r;
		cin >> l >>r;
		p[l].push_back(r);
		p[r].push_back(l);
	}
	dfs(1);
	if(son[t1] != son[t2])
	{
		cout<<t1<<" "<<p[t1][0]<<"\n";
		cout<<t1<<" "<<p[t1][0]<<"\n";
	}
	else
	{
		int u = 0;
		for(int i = 0;i < p[t1].size();i++)
		{
			int j = p[t1][i];
			if(j!=t2)
			{
				u = j;
				break;
			}
		}
		cout<<t1<<" "<<u<<"\n";
		cout<<t2<<" "<<u<<"\n";
		
	}
}
int main()
{
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	}
}
//
//

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值