首先我们要有一个明确的构造思路
对于非根节点,我们把子树连上来的线两两配对,这样如果它有奇数个子树就会剩一个,这时候把这根线传给父亲即可。对于根节点还是两两配对,但是注意如果它也有奇数个子树就不能剩了,必须把这根线算上。这样第一问的答案就是每个非根节点贡献度数除以二下取整,根节点贡献度数除以二上取整
第二问我们先二分答案,仍然沿用这个思路,这时我们要让最长的最短,于是我们每次把子树里传上来的线塞进一个multiset。讨论:对于有奇数个子树的情况,从大到小枚举线,二分出和当前的线拼起来不超过二分的$mid$的最长的线,然后把它们删掉,最后剩下的那根传给父亲。注意如果有一根线找不到配对也不一定失败,我们先视为把它传给父亲,然后看看剩下的还能不能拼好。对于有偶数个子树的情况,我们直接塞一个零进multiset里去,然后按奇数的方法做,这是等价的。对于根节点,我们去除掉塞零和留线这两个操作判定即可。
1 #include<set> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=10005; 7 int p[N],noww[2*N],goal[2*N],deg[N],dp[N]; 8 int n,t1,t2,cnt,mins,l,r,mid,ans; 9 multiset<int> se; 10 multiset<int>::iterator it; 11 void link(int f,int t) 12 { 13 noww[++cnt]=p[f]; 14 goal[cnt]=t,p[f]=cnt; 15 } 16 bool check(int nde,int fth) 17 { 18 if(deg[nde]==1&&nde!=1) 19 {dp[nde]=0; return true;} 20 for(int i=p[nde];i;i=noww[i]) 21 if(goal[i]!=fth&&!check(goal[i],nde)) 22 return false; 23 se.clear(); 24 for(int i=p[nde];i;i=noww[i]) 25 if(goal[i]!=fth) 26 se.insert(dp[goal[i]]+1); 27 if(nde==1) 28 { 29 while(se.size()>1) 30 { 31 long long mem=*(--se.end()); 32 se.erase(--se.end()); it=se.upper_bound(mid-mem); 33 if(it!=se.begin()) it--; else return false; 34 if((*it)+mem>mid) return false; se.erase(it); 35 } 36 if(se.size()) return (*se.begin())<=mid; 37 else return true; 38 } 39 else 40 { 41 int must=0; 42 if(se.size()%2==0) se.insert(0); 43 while(se.size()>1) 44 { 45 long long mem=*(--se.end()); 46 se.erase(--se.end()); it=se.upper_bound(mid-mem); 47 if(it!=se.begin()) it--; else return false; 48 if((*it)+mem>mid) 49 { 50 if(must) return false; 51 else 52 { 53 must=*(--se.end()); 54 continue; 55 } 56 } 57 else se.erase(it); 58 } 59 return (dp[nde]=must?must:(*se.begin()))<=mid; 60 } 61 return true; 62 } 63 int main () 64 { 65 scanf("%d",&n); 66 for(int i=1;i<n;i++) 67 { 68 scanf("%d%d",&t1,&t2); 69 link(t1,t2),link(t2,t1); 70 deg[t1]++,deg[t2]++; 71 } 72 mins+=(deg[1]+1)/2; 73 for(int i=2;i<=n;i++) 74 mins+=(deg[i]-1)/2; 75 l=1,r=n; 76 while(l<=r) 77 { 78 mid=(l+r)/2; 79 if(check(1,0)) r=mid-1,ans=mid; 80 else l=mid+1; 81 } 82 printf("%d %d",mins,ans); 83 return 0; 84 }