题意:
给你一棵树,你可以删除一条边,添加一条边(可以删除添加同一条),使得这棵树的重心唯一,输出删除的边,和添加的边。
前置知识:
树的重心及性质
- 首先要知道什么是树的重心,树的重心就是:树中的一个节点,他满足,删除这个节点,剩下若干个连通块,这若干个连通块中最大的那个最小。
- 重心一些基本性质有:
- 删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
- 树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
- 两个树通过一条边合并,新的重心在原树两个重心的路径上;
- 树删除或添加一个叶子节点,重心最多只移动一条边;
- 一棵树最多有两个重心,且相邻。
重心的求法:
- 把无根树化成有根树,求出以每个节点为根节点,子树的大小记作 s z [ i ] sz[i] sz[i],那么每个节点作为根节点最大的子树的大小就是 m a x ( s z [ i ] − 1 , s z [ f a ] − s z [ i ] ) max(sz[i]-1,sz[fa]-sz[i]) max(sz[i]−1,sz[fa]−sz[i])。
题解:
求出重心,如果有两个,那么这两个必然在同一条边上(由重心性质可知),我把其中重心的子分支挪到另一个子分支上,就可以了
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
vector<int>G[100005];
int f[100005],sz[100005],mx[100005];
void dfs(int u,int fa) {
f[u] = fa;
sz[u] = 1;
for(auto v : G[u]) {
if(v != fa) {
dfs(v,u);
sz[u] += sz[v];
mx[u] = max(mx[u],sz[v]);
}
}
}
int main()
{
int T;
cin >> T;
while(T--)
{
int n;
cin >> n;
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
dfs(1,0);
int inf = 1e9,core;
for(int i=1;i<=n;i++) {
mx[i] = max(mx[i],sz[1]-sz[i]);
if(mx[i] < inf) {
inf = mx[i];
core = i;
}
}
int flag = 0;
vector<int>ans;
for(int i=1;i<=n;i++)
{
if(mx[i] == inf)ans.pb(i);
}
if(ans.size() == 1) {
cout<<ans[0]<<' '<<G[ans[0]][0]<<'\n';
cout<<G[ans[0]][0]<<' '<<ans[0]<<'\n';
} else {
int tmp;
for(auto v : G[ans[0]]) {
if(v != ans[1])
{
tmp = v;
break;
}
}
cout<<ans[0]<<' '<<tmp<<'\n';
cout<<tmp<<' '<<ans[1]<<'\n';
}
for(int i=1;i<=n;i++)
{
sz[i] = 0;
mx[i] = 0;
G[i].clear();
f[i] = 0;
}
}
}