题意:
对于一棵树 删除一条边再加上一条边使得新树只有一个重心
输入
输入由多个测试案例组成。第一行包含一个整数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();
}
}
//
//