其实这题我挺不喜欢的,大部分人都是靠才结论过的吧??
树的重心:删掉这个节点,形成的最大连通块最小
所以可以先 d f s dfs dfs一遍求出重心
若重心只有一个,随便删掉一条边再连回去
否则最多有两个重心,且相邻
Ⅰ . 为 什 么 只 有 两 个 重 心 \color{Red}Ⅰ.为什么只有两个重心 Ⅰ.为什么只有两个重心
很抱歉,在网上找了一圈没看到有人证过,然后我自己也很菜~
我在纸上画了一下,似乎如果有 3 3 3个重心必须成环形成对称的连通块大小才行
树是不能成环的
Ⅱ . 为 什 么 这 两 个 重 心 相 邻 ( 一 下 翻 译 自 官 方 题 解 ) \color{Red}Ⅱ.为什么这两个重心相邻(一下翻译自官方题解) Ⅱ.为什么这两个重心相邻(一下翻译自官方题解)
设 x , y x,y x,y是重心
如果 x , y x,y x,y的路径上有其他节点,那么那些节点更可能成为重心,而不是 x , y x,y x,y
设 x x x是 y y y的父亲,可以知道 y y y的子树大小是 n 2 \frac{n}{2} 2n(这个在纸上画一下)
这样我们从 y y y的子树中剪下一个叶子,连到 x x x上面去
这样 x x x就变成了唯一的重心,此时删掉 y y y形成的最大连通块变成 n 2 + 1 \frac{n}{2}+1 2n+1
而删掉 x x x形成的最大连通块仍是 n 2 \frac{n}{2} 2n
代码中,我是直接把 y y y的一个儿子减下来连到 x x x上面去的
#include
using namespace std;
const int maxn=4e5+10;
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){
d[++cnt]=(edge){
v,head[u]},head[u]=cnt; }
int siz[maxn],minn=1e9,xin[maxn],n,t,p;
void dfs(int u,int fa)
{
siz[u]=1;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( v==fa ) continue;
dfs(v,u);
siz[u]+=siz[v];
xin[u]=max(xin[u],siz[v]);
}
xin[u]=max(xin[u],(n-siz[u]) );
minn=min( minn,xin[u] );//找删掉节点后最大连通块最小的数值
}
int main()
{
cin >> t;
while( t-- )
{
cin >> n;
int l,r; minn=1e9;
for(int i=1;i<n;i++)
{
cin >> l >> r;
add(l,r); add(r,l);
}
dfs(1,0);
int x=0,y=0;
for(int i=1;i<=n;i++)
if( xin[i]==minn )
{
if( x ) y=i;
else x=i;
}
if( x&&!y )
{
cout << l << " " << r << endl;
cout << l << " " << r << endl;
}
else//2个重心
{
if( siz[x]<siz[y] ) swap(x,y);
int oo=0;
for(int i=head[y];i;i=d[i].nxt )
if( d[i].to!=x&&d[i].to!=y ) oo=d[i].to;
cout << oo << " " << y << "\n";
cout << oo << " " << x << "\n";
}
cnt=1;
for(int i=0;i<=n;i++) head[i]=xin[i]=0;
}
}