给定一棵树,要求在用尽可能少的链覆盖所有的边的前提下使所有链的长度和尽可能小,求最小和。
从终态看,偶数个叶子,每条边被遍历的次数很容易确定,奇数个叶子,可以省掉以某个叶子为为一端的路径。
具体实现:先以某个叶子为根,求出每条边被遍历次数,然后考虑翻转根到某个点路径上的边(1->2,2->1)可以得到的最优贡献。
PS:因为是和bblss123一起做的所以很有纪念意义。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cassert>
#include<cstdio>
#include<bitset>
#include<queue>
#include<cmath>
#include<ctime>
#define inf (1<<30)
#define INF (1ll<<62)
#define prt(x) cout<<#x<<":"<<x<<" "
#define prtn(x) cout<<#x<<":"<<x<<endl
using namespace std;
typedef long long ll;
template<class T>void sc(T &x){
int f=1;x=0;char c;
while(c=getchar(),c<48)if(c=='-')f=-1;
do x=x*10+(c^48);
while(c=getchar(),c>47);
x*=f;
}
template<class T>void nt(T x){
if(!x)return;
nt(x/10);putchar('0'+x%10);
}
template<class T>void pt(T x){
if(x<0)putchar('-'),x=-x;
if(!x)putchar('0');
else nt(x);
}
const int maxn=100006;
int n;
int last[maxn],ecnt;
int deg[maxn];
struct Edge{
int to,nxt;
}e[maxn<<1];
int v[maxn];
void ins(int u,int v){
deg[u]++;
e[ecnt]=(Edge){v,last[u]};last[u]=ecnt++;
}
int ans,RT,mxdis;
int dfs(int x,int f,int eid){
int scnt=0;
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==f)continue;
scnt+=dfs(to,x,i>>1);
}
if(x==RT)return scnt;
if(!scnt)scnt++;
if(scnt&1){
ans++;
v[eid]=1;
}
else{
ans+=2;
v[eid]=2;
}
return scnt;
}
void dfs1(int x,int f,int s){
if(s>mxdis)mxdis=s;
for(int i=last[x];i!=-1;i=e[i].nxt){
int to=e[i].to;
if(to==f)continue;
dfs1(to,x,v[i>>1]==1?s-1:s+1);
}
}
void solve(){
sc(n);
memset(last,-1,n+1<<2);
memset(deg,0,n+1<<2);
ecnt=0;
for(int u,v,i=1;i<n;i++){
sc(u);sc(v);
ins(u,v);ins(v,u);
}
for(int i=1;i<=n;i++)
if(deg[i]==1){
RT=i;
break;
}
ans=0;
int sum=dfs(RT,0,-1)+1;
if(!(sum&1)){
pt(ans);putchar('\n');
return;
}
mxdis=0;
dfs1(RT,0,0);
ans-=mxdis;
pt(ans);putchar('\n');
}
int main(){
// freopen("pro.in","r",stdin);
// freopen("chk.out","w",stdout);
int cas;sc(cas);
while(cas--)solve();
return 0;
}