BZOJ2097

题意:最多去掉一个树上的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.

转载于:https://www.cnblogs.com/exponent/archive/2011/08/14/2137987.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值