Codeforces Round #364 (Div. 1) B Connecting Universities

3 篇文章 0 订阅
1 篇文章 0 订阅

原题网址:http://codeforces.com/contest/700/problem/B
这里写图片描述
这里写图片描述
这道题有一个补集思想,对于一棵有根树,两点x,y之间的路径长可以看作两点之间的到根距离减去lca(x,y)到根的距离。假设根已经确定,要使两两的lca到根路径和最小,最小的情况是lca都是根节点,假如根节点的含特殊点最多的一棵子树所含特殊点<=k,那么肯定存在一种配对方案,使得每组点对之间的lca都是根节点,否则肯定存在若干对点对是同属于这棵子树的,lca便不再是根节点,lca到根节点的距离需要从答案中减去。再递归做这棵子树。

const
  mxn=200000;
type
  point=record
          y,next:longint;
        end;  
var
  map:array[0..mxn*2+50] of point;
  first,size:array[0..mxn+50] of longint;
  n,k,i,x,y,s:longint;
  ans:int64;
procedure ins(x,y:longint);
  begin
    inc(s);map[s].y:=y;
    map[s].next:=first[x];first[x]:=s;
  end;
procedure dfs1(x,fa,dep:longint);
  var
    t:longint;
  begin
    if size[x]=1 then inc(ans,dep);//将所有特殊点的深度加起来
    t:=first[x];
    while t>0 do
      begin
        if map[t].y<>fa then
          begin
            dfs1(map[t].y,x,dep+1);
            inc(size[x],size[map[t].y]);
          end;  
        t:=map[t].next;
      end;
  end;
procedure dfs2(x,fa:longint);
  var
    t,mxsz,mxi:longint;
  begin
    t:=first[x];mxsz:=0;
    while t>0 do//找到最大的子树
      begin
        if (map[t].y<>fa)and(size[map[t].y]>mxsz) then
          begin
            mxsz:=size[map[t].y];
            mxi:=map[t].y;
          end;
        t:=map[t].next;
      end;
    if mxsz*2>size[1] then
      begin
        dec(ans,size[mxi]*2-size[1]);//从答案中减去lca到根距离
        dfs2(mxi,x);
      end;
  end;
begin
  readln(n,k);
  ans:=0;
  for i:=1 to k<<1 do
    begin
      read(x);
      size[x]:=1;
    end;
  fillchar(first,sizeof(first),0);s:=0; 
  for i:=1 to n-1 do
    begin
      read(x,y);
      ins(x,y);
      ins(y,x);
    end;
  ans:=0;   
  dfs1(1,0,0);
  dfs2(1,0);
  writeln(ans);
end.

刚才考虑了如果存在一棵子树特殊点数量超过k的情况,如果将树的重心(此处的“重心”定义为特殊点最多的子树特殊点数量不超过k的点)设为根就可以避免这种情况。如果根为重心,必然存在一种配对方案,使得任一点对lca都为根节点,只要将特殊点的深度相加即可。

type
  point=record
          y,next:longint;
        end;
var
  map:array[0..500000] of point;
  cnt,size,first:array[0..200050] of longint;
  n,k,i,x,y,root,t,mxsz,mxi,s:longint;
  ans:int64;
procedure ins(x,y:longint);
  begin
    inc(s);map[s].y:=y;
    map[s].next:=first[x];first[x]:=s;
  end;
procedure dfs(x,fa:longint);
  var
    t:longint;
  begin
    t:=first[x];
    while t>0 do
      begin
        if map[t].y<>fa then
          begin
            dfs(map[t].y,x);
            inc(size[x],size[map[t].y]);
          end;  
        t:=map[t].next;
      end;
  end;
procedure dfs2(x,dep,fa:longint);
  var
    t:longint;
  begin
    if cnt[x]=1 then inc(ans,dep);
    t:=first[x];
    while t>0 do
      begin
        if map[t].y<>fa
          then dfs2(map[t].y,dep+1,x);
        t:=map[t].next;  
      end;
  end;
begin
  readln(n,k);
  for i:=1 to k<<1 do
    begin
      read(x);
      cnt[x]:=1;
      size[x]:=1;
    end;
  for i:=1 to n-1 do
    begin
      read(x,y);
      ins(x,y);
      ins(y,x);
    end;
  root:=1;
  dfs(root,0);
  repeat//找重心
    t:=first[root];
    mxsz:=0;
    while t>0 do
      begin
        if size[map[t].y]>mxsz then
          begin
            mxsz:=size[map[t].y];
            mxi:=map[t].y;
          end;
        t:=map[t].next;  
      end;
    if mxsz*2>size[root]
      then
        begin
          size[root]:=size[root]-size[mxi];
          size[mxi]:=size[mxi]+size[root];
          root:=mxi;
        end
      else break;
  until false;
  dfs2(root,0,0);
  writeln(ans);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值