LuoguP2680运输计划

P2680 运输计划

  题意:给定一棵 n 个点的无根树,经过第 i 条边需要消耗 ti 的时间。你可以选择一条边将其通过耗时置为 0。
  有 m 辆飞船,第 j 辆飞船从点 sj 飞到点 tj,求最后一艘飞船最早到达的时刻。

1.60pts: O(n^2)暴力+(m=1)

  n^2:暴力dfs出每条路径经过的边,枚举被置为0的边并对每一条路径进行计算。
  m=1:找出路径上的最长边即可。

2.100pts:树链剖分

- 先考虑一条链的

 最大值最小的问题可以二分答案。
 假设最长区间的长度为 mx,二分的答案为 ans,则所有长度大于 ans 的区间必须包含被删的边,且被删的边长度大于 mx-ans。

- 再考虑一棵树

 把每一条路径都看成一条树链,也运用二分答案。
 采用链上的方法,只是用树链剖分将路径变为区间,再用树上差分代替序列差分就可以了 
var head,size,dep,top,sum,sum1,value,father,son:array[-1..310005] of int64;
    next,vet,val,from:array[-1..650005] of longint;
    lca,dis,ll,rr:array[-1..650005] of int64;
    i,j,n,m,l,r,ans,mid,tot,maxdis,u1,v1,cost1:longint;
procedure add(u,v,cost:longint);//链式前向星建图
begin
  inc(tot);
  next[tot]:=head[u];
  val[tot]:=cost;
  from[tot]:=u;
  vet[tot]:=v;
  head[u]:=tot;
end;
procedure dfs1(u:longint);//第一遍dfs,求出点u的父节点,子树中节点个数和点u的深度、重儿子
var e:longint;
begin
  size[u]:=1; e:=head[u];
  while e<>0 do
  begin
    if dep[vet[e]]=0 then
    begin
      dep[vet[e]]:=dep[u]+1;
      father[vet[e]]:=u;
      dfs1(vet[e]);
      size[u]:=size[u]+size[vet[e]];
      if size[vet[e]]>size[son[u]] then son[u]:=vet[e];
    end;
    e:=next[e];
  end;
end;
procedure dfs2(u,fa:longint);//求出根到u的路径长度和点u处于的重链的顶端顶点
var e:longint;
begin
  sum[u]:=sum[father[u]]+value[u]; top[u]:=fa;
  if son[u]=0 then exit;
  dfs2(son[u],fa);
  e:=head[u];
  while e<>0 do
  begin
    if (vet[e]<>father[u]) and (vet[e]<>son[u]) then dfs2(vet[e],vet[e]);
    e:=next[e];
  end;
end;
procedure dfs3(u:longint);//树上差分
var e:longint;
begin
  e:=head[u];
  while e<>0 do
  begin
    if vet[e]<>father[u] then
    begin
      dfs3(vet[e]);
      inc(sum1[u],sum1[vet[e]]);
    end;
    e:=next[e];
  end;
end;
function max(x,y:longint):longint;
begin
  if x>y then exit(x);
  exit(y);
end;
function ask(x,y:longint):longint;//树链剖分求LCA
begin
  while top[x]<>top[y] do
  begin
    if dep[top[x]]<dep[top[y]] then y:=father[top[y]] else x:=father[top[x]];
  end;
  if dep[x]<dep[y] then exit(x)
  else exit(y);
end;
function check(k:longint):boolean;
var i,num:longint;
begin
  fillchar(sum1,sizeof(sum1),0); maxdis:=0; num:=0;
  for i:=1 to m do
  if dis[i]>k then
  begin
    inc(num);
    inc(sum1[ll[i]]);
    inc(sum1[rr[i]]);
    dec(sum1[lca[i]],2);//树上差分
    maxdis:=max(maxdis,dis[i]-k);
  end;
  dfs3(1);
  for i:=1 to n do
  if (sum1[i]=num) and (value[i]>=maxdis) then exit(true);
  exit(false);
end;
begin
  readln(n,m);
  for i:=1 to n-1 do
  begin
    readln(u1,v1,cost1);
    add(u1,v1,cost1);
    add(v1,u1,cost1);
  end;
  dep[1]:=1; father[1]:=1; dfs1(1);
  for i:=1 to tot do
  if dep[from[i]]>dep[vet[i]] then value[from[i]]:=val[i]
  else value[vet[i]]:=val[i];
  dfs2(1,1);
  for i:=1 to m do
  begin
    readln(ll[i],rr[i]);
    lca[i]:=ask(ll[i],rr[i]);
    dis[i]:=sum[ll[i]]+sum[rr[i]]-sum[lca[i]]*2;
    maxdis:=max(maxdis,dis[i]);
  end;
  l:=0; r:=maxdis;
  while l<=r do
  begin
    mid:=(l+r) div 2;
    if check(mid) then begin ans:=mid; r:=mid-1 end
    else l:=mid+1;
  end;//二分
  writeln(ans);
end.
posted @ 2021-10-16 19:32  星域之辉  阅读(48)  评论(0)    收藏  举报