因
为
题
目
保
证
输
入
的
是
一
颗
树
,
所
以
我
们
可
以
d
f
s
寻
找
.
,
我
们
要
找
的
方
案
肯
定
的
树
,
肯
定
是
非
两
端
的
链
的
子
树
的
深
度
一
定
不
超
过
2
,
因
为
超
过
2.
那
么
一
定
会
构
成
环
,
手
画
就
可
以
证
明
了
因为题目保证输入的是一颗树,所以我们可以dfs寻找.,我们要找的方案肯定的树,肯定是非两端的链的子树的深度一定不超过2,因为超过2.那么一定会构成环,手画就可以证明了
因为题目保证输入的是一颗树,所以我们可以dfs寻找.,我们要找的方案肯定的树,肯定是非两端的链的子树的深度一定不超过2,因为超过2.那么一定会构成环,手画就可以证明了
那
么
问
题
就
转
化
为
找
出
一
条
链
,
链
上
每
一
个
连
出
去
的
点
都
不
能
再
连
的
最
大
树
那么问题就转化为找出一条链,链上每一个连出去的点都不能再连的最大树
那么问题就转化为找出一条链,链上每一个连出去的点都不能再连的最大树
数
学
表
示
就
是
∑
i
=
1
k
o
u
t
[
k
]
−
(
k
−
2
)
最
小
,
因
为
链
上
除
端
点
外
的
点
都
被
多
算
了
一
次
,
(
每
个
链
上
的
点
都
会
被
他
前
一
个
点
和
后
一
个
点
算
一
次
)
数学表示就是\sum_{i=1}^{k}out[k]-(k-2)最小,因为链上除端点外的点都被多算了一次,(每个链上的点都会被他前一个点和后一个点算一次)
数学表示就是∑i=1kout[k]−(k−2)最小,因为链上除端点外的点都被多算了一次,(每个链上的点都会被他前一个点和后一个点算一次)
这样类似求重心地求就行了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=3e5+5;
int Q,n;
struct edge{
int link,v;
}q[N<<1];
int head[N],cnt=0,in[N],out[N],root;
void put(int u,int v){ q[++cnt].v=v;q[cnt].link=head[u];head[u]=cnt;}
int ans,ansst,now=0;
void dfs1(int s,int fa){
if(now>ans){
ans=now;
ansst=s;
}
for(int i=head[s];i;i=q[i].link){
int v=q[i].v;
if(v==fa) continue;
now+=out[v];
dfs1(v,s);
now-=out[v];
}
}
void dfs2(int s,int fa){
if(now>ans){
ans=now;
}
for(int i=head[s];i;i=q[i].link){
int v=q[i].v;
if(v==fa) continue;
now+=out[v];
dfs2(v,s);
now-=out[v];
}
}
int main(){
scanf("%d",&Q);
while(Q--){
cnt=0; ansst=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){out[i]--;in[i]=0,out[i]=-1,head[i]=0,q[i].link=0,q[i].v=0;}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
put(u,v),put(v,u);
in[v]++;out[u]++;out[v]++;
}
for(int i=1;i<=n;i++)if(!in[i]) {root=i;break;} ans=0,now=0;
now+=out[root];
dfs1(root,0);
now=0;
now+=out[ansst];
dfs2(ansst,0);
printf("%d\n",ans+2);
}
}