题意:最多去掉一个树上的s条边,将树分割成S+1块,使得所有块的最长链的最大值最小.
USACO官方的题解讲的很清楚..
First, conduct a binary search on the answer, D. To do this, just find a way to check, for any D, if it is possible to make S cuts such that each tree has diameter at most D.
We will give a greedy algorithm which will compute the smallest number of cuts needed if each subtree is to have diameter at most D.
Root the tree arbitrarily.
Suppose we are processing a vertex A with children C1, C2, ..., Ck, and suppose that any necessary cuts have been made to edges below these k vertices. Let the depth of a vertex be the maximum length from that vertex down to a leaf.
The diameter of the subtree rooted at A is the maximum of depth(Ci) + depth(Cj) + 2, over all distinct i and j, if the maximal diameter goes through A, or the diameter of the subtree rooted at Ci for some i. Since we are assuming all lower cuts have been made, we actually just need to ensure that depth(Ci) + depth(Cj) + 2 is at most D.
If it is at most D, then there is certainly no reason to make any more cuts within this tree. If it is greater than D, then we need to cut off some edges between A and its children. We want to cut off children of maximum depth (because given a choice between cutting off two vertices, it is always better to cut off the one which would contribute the most to a large diameter), and we cut off children until the maximum value of depth(Ci) + depth(Cj) + 2 is at most D.
Perform a depth first search through the tree, and for each node, process its children before processing itself.
Meanwhile, keep track of the total number of cuts and then check if this value is less than or equal to S. This algorithm is O(n log2 n). One log factor is for the binary search, the n factor is for traversing the tree, and another log factor is for sorting the depths of a nodes children.
Notice how this second log factor is in fact very small, since most nodes will have a small number of children. (In fact, if you are careful enough, you can cut off this extra log factor entirely.)
code:
/************************************************************** Problem: 2097 User: exponent Language: Pascal Result: Accepted Time:732 ms Memory:7280 kb ****************************************************************/ {$M 10000000} type edge=record v,n:longint; end; const maxn=100001; maxm=200001; var ex:array[0..maxm] of edge; e:array[0..maxn] of edge; head,headx,h,d:array[0..maxn] of longint; vis:array[0..maxn] of boolean; n,s,i,u,v,l,r,mid,cnt,cntx,size,count:longint; procedure add(u,v:longint); begin inc(cnt); e[cnt].v:=v; e[cnt].n:=head[u]; head[u]:=cnt; end; procedure addx(u,v:longint); begin inc(cntx); ex[cntx].v:=v; ex[cntx].n:=headx[u]; headx[u]:=cntx; end; procedure buildtree(u:longint); var v,p:longint; begin vis[u]:=true; p:=headx[u]; while p<>0 do begin v:=ex[p].v; if not vis[v] then begin add(u,v); buildtree(v); end; p:=ex[p].n; end; end; procedure swap(a,b:longint); var temp:longint; begin temp:=h[a]; h[a]:=h[b]; h[b]:=temp; end; procedure down(k:longint); var p:longint; begin while k<=size>>1 do begin p:=k<<1; if (p<size)and(h[p]<h[p+1]) then inc(p); if h[k]<h[p] then begin swap(k,p); k:=p; end else break; end; end; procedure up(k:longint); begin while (k>1)and(h[k>>1]<h[k]) do begin swap(k,k>>1); k:=k>>1; end; end; procedure ins(new:longint); begin inc(size); h[size]:=new; up(size); end; procedure del(k:longint); begin swap(k,size); dec(size); down(k); end; function gettop:longint; begin gettop:=h[1]; del(1); end; procedure dfs(u,lim:longint); var v,p,c,top:longint; begin p:=head[u]; while p<>0 do begin v:=e[p].v; dfs(v,lim); p:=e[p].n; end; size:=0; p:=head[u]; while p<>0 do begin v:=e[p].v; ins(d[v]); p:=e[p].n; end; if size=0 then begin d[u]:=0; exit; end; if size=1 then begin if h[1]>=lim then begin d[u]:=0; inc(count); end else d[u]:=h[1]+1; exit; end; while size>=2 do begin top:=gettop; if top+h[1]+2>lim then inc(count) else begin ins(top); break; end; end; if h[1]>=lim then inc(count) else d[u]:=h[1]+1; end; function check(middle:longint):boolean; begin fillchar(d,sizeof(d),0); count:=0; dfs(1,middle); exit(count<=s); end; begin readln(n,s); for i:=1 to n do begin readln(u,v); addx(u,v); addx(v,u); end; buildtree(1); l:=0; r:=maxn; while l<r do begin mid:=(l+r)>>1; if check(mid) then r:=mid else l:=mid+1; end; writeln(l); end.