Description
给定一棵树,求能够使树重心唯一的删一条边加一条边的可行方案。
Solution
只需要考虑重心个数为2的情况。
当树重心个数为2时,重心一定相邻,且大小为\(\frac{n}{2}\),其余节点的最大子树均\(>\frac{n}{2}\)。
假设两个重心为u,v, u为v的孩子。
只要删去u子树(v的最大子树中u贡献的部分)中任意一个叶子接到v上即可。
此时,v的最大子树大小-1\(<\frac{n}{2}\),u的最大子树大小+1\(>\frac{n}{2}\),其余节点的最大子树均\(\geq\frac{n}{2}\),v变为唯一的树重心。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct graph{
int nxt,to;
}e[N<<1];
int g[N],cen[2],w[N],fa[N],siz[N],n,t,cnt;
void adde(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
void dfs(int u){
siz[u]=1;
for(int i=g[u];i;i=e[i].nxt)
if(e[i].to!=fa[u]){
fa[e[i].to]=u;
dfs(e[i].to);
siz[u]+=siz[e[i].to];
w[u]=max(siz[e[i].to],w[u]);
}
w[u]=max(w[u],n-siz[u]);
}
int findleaf(int u){
for(int i=g[u];i;i=e[i].nxt)
if(e[i].to!=fa[u])
return findleaf(e[i].to);
return u;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
cnt=0;
fill(g+1,g+1+n,0);
fill(w+1,w+1+n,0);
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);
}
dfs(1);
int ans=n+1;bool flag=true;
for(int i=1;i<=n;++i)
if(w[i]<ans){
ans=w[i];cen[0]=i;flag=true;
}
else if(w[i]==ans){
cen[1]=i;flag=false;
}
if(flag){
printf("%d %d\n",2,fa[2]);
printf("%d %d\n",2,fa[2]);
}
else{
int u=cen[0],v=cen[1];
if(u==fa[v]) swap(u,v);
//对于重心u而言,另外一个重心在“向上”的那棵子树
u=findleaf(u);
printf("%d %d\n",u,fa[u]);
printf("%d %d\n",u,v);
}
}
return 0;
}