分析:
玩过一笔画类型游戏的人都知道,本题第一问答案为(∑(deg[i]&1))/2。
但是更能揭示这道题做法的公式是∑((deg[i]-1)/2)+1。
可以理解为把每一个节点的子树通过删链使其成为一条链。
于是我们就可以愉快地分治+二分答案了。
注意当一个节点有偶数个孩子节点时需要补一条长为0的链。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
const int MAXN=10005;
int n,ecnt,head[MAXN],deg[MAXN];
int len[MAXN],b[MAXN],siz;
struct Edge{
int to,nxt;
}e[MAXN<<1];
inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
inline bool check2(int mid,int siz,int lim){
int lp=1,rp=siz;
if(lp==mid) lp++;
if(rp==mid) rp--;
while(lp<rp){
if(b[lp]+b[rp]>lim) return 0;
lp++;rp--;
if(lp==mid) lp++;
if(rp==mid) rp--;
}
return 1;
}
bool dfs(int x,int pre,int lim){
bool isleaf=1;
for(int i=head[x];i;i=e[i].nxt){
int ver=e[i].to;
if(ver==pre) continue;
isleaf=0;
if(!dfs(ver,x,lim)) return 0;
}
if(isleaf){
len[x]=1;
return 1;
}
siz=0;
for(int i=head[x];i;i=e[i].nxt){
int ver=e[i].to;
if(ver==pre) continue;
b[++siz]=len[ver];
}
if(x==1){
std::sort(b+1,b+siz+1);
if(siz&1){
if(b[siz]>lim) return 0;
siz--;
}
int lp=1,rp=siz;
while(lp<rp){
if(b[lp]+b[rp]>lim) return 0;
lp++;rp--;
}
return 1;
}
if(!(siz&1)) b[++siz]=0;
std::sort(b+1,b+siz+1);
int l=1,r=siz,res=-1;
while(l<=r){
if(b[l]>=lim) return 0;
int mid=((l+r)>>1);
if(check2(mid,siz,lim)) res=mid,r=mid-1;
else l=mid+1;
}
if(res==-1||b[res]>=lim) return 0;
len[x]=b[res]+1;
return 1;
}
inline bool check1(int mid){
return dfs(1,0,mid);
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
deg[u]++;deg[v]++;
}
// int ans1=1;
// for(int i=1;i<=n;i++)
// ans1+=(deg[i]-1)/2;
int ans1=0;
for(int i=1;i<=n;i++)
ans1+=(deg[i]&1);//两种求法均可。
printf("%d ",ans1>>1);
int l=1,r=10000,ans2;
while(l<=r){
int mid=((l+r)>>1);
if(check1(mid)) ans2=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans2);
return 0;
}