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.