树上倍增——(货车运输 解题报告)

Posted by-w

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树上倍增——(货车运输 解题报告)相关的知识,希望对你有一定的参考价值。

倍增新高度——树上倍增(其实差不多啦)

首先倍增就不说了

那么树上倍增和倍增有什么区别呢?

其实没什么区别,对于树上的结点u,

我们同样用st[u][l]数组记录其结点u向上2^l步数中权值最小(最大)的值

但是树上的边不是连续的啊,这我们怎么去维护呢?

这时,我们需要引入一个辅助数组f数组,

对于树上结点u,这个f数组f[u][l]表示这个点u向上走2^l次走到那个点

(这里有一个小技巧:把根的父亲设为根,防止跑到树的外面)

那么就很简单了,伪代码如下:

for  i:=1 to n do
begin
  f[i][0]:=fa[i]; //fa表示i的父亲
  st[i][0]:=w[i];//w表示结点i到它父亲这条边的权值
end;//预处理
for j:=1 to lg[n] do
begin
  for i:=1 to n do
  begin
     f[i][j]:=f[f[i][j-1]][j-1];     //向当前结点向上跳2^j
    st[i][j]:=opt(st[i][j-1],st[f[i][j-1]][j-1]);    //合并最值
  end;
end;

那么树上倍增可以干嘛呢???

可以用来维护树上任意两点之间最值,求lca

lca代码如下:

{$inline on}
var
  u,v,cnt,i,j,n,m,q,x,y,l,root:longint;
  head,tail,vet,next,deep,fa,lg:array[0..1001000] of longint;
  f:array[0..501000,0..30] of longint;
function min(a,b:longint):longint; inline;
begin
  if a>b then exit(b)
  else exit(a);
end;
procedure solve(u,v:longint); inline;
var y,ans:longint;
begin
  ans:=maxlongint;
  if deep[u]<deep[v] then 
  begin
    y:=u; u:=v; v:=y;
  end;
  l:=lg[n];
  while deep[u]>deep[v] do 
  begin
    while (deep[f[u][l]]<deep[v]) and (l<>0) do dec(l);
    u:=f[u][l];
  end;
  if u=v then writeln(u)
  else
  begin
    l:=lg[n];
    while u<>v do
    begin
      while (f[u][l]=f[v][l]) and (l<>0) do dec(l);
      u:=f[u][l]; v:=f[v][l];
    end;
    writeln(u);
  end;
end;
procedure dfs(now,father:longint);
var point:longint;
begin
  if father<>0 then
  begin
    deep[now]:=deep[father]+1;
    fa[now]:=father;
  end;
  point:=head[now];
  while point<>0 do
  begin
    if vet[point]<>father then dfs(vet[point],now);
    point:=next[point];  
  end; 
end;
procedure GG;
var i:longint;
begin
  lg[1]:=0;
  for i:=2 to n do
  lg[i]:=lg[i div 2]+1;
end;
begin
  readln(n,q,root); GG;
  for i:=1 to n-1 do
  begin
    readln(u,v);
    inc(cnt);
    vet[cnt]:=v; next[cnt]:=head[u]; head[u]:=cnt;
    inc(cnt);
    vet[cnt]:=u; next[cnt]:=head[v]; head[v]:=cnt;
  end;
  fa[root]:=root; deep[root]:=1; dfs(root,0);
  for i:=1 to n do f[i][0]:=fa[i];
  for j:=1 to lg[n] do
  begin
    for i:=1 to n do
      f[i][j]:=f[f[i][j-1]][j-1];
  end;
  for i:=1 to q do
  begin
    readln(u,v);
    solve(u,v);
  end;
end.

给出一道例题:

货车运输

题目描述

AA国有nn座城市,编号从 11到nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式:

 

第一行有两个用一个空格隔开的整数n,mn,m,表示 AA 国有nn 座城市和 mm 条道路。

接下来 mm行每行33个整数 x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从 xx号城市到yy号城市有一条限重为 zz 的道路。注意: xx 不等于 yy,两座城市之间可能有多条道路 。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y 。

 

输出格式:

 

共有 qq 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1?1。

 

输入输出样例

输入样例#1: 复制
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1: 复制
3
-1
3

说明

对于 30\%30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,0000<n<1,000,0<m<10,000,0<q<1,000;

对于 60\%60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,0000<n<1,000,0<m<50,000,0<q<1,000;

对于 100\%100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,0000<n<10,000,0<m<50,000,0<q<30,000,0z100,000。

这道题要求图中两点路径之间最小值最大,这个性质之间让我们想到了最大生成树

我在这里简单证明一下:

若我们对于原图已经得到了其最大生成树

假设边的最小值不在最大生成树上,那么这条边的权值必然大于生成树中一条边的权值

我们把它加入生成树,就会得到比原来更优的生成树,和原来的假设冲突了

那么这条边必然应该在最大生成树上

则此题便成为了一道模板题(老师说的,一定是我太菜了QwQ)

对于原图跑一边最大生成树

用倍增维护树上两点间的最小权值(lca,最小生成树,倍增,似乎全是模板诶)

代码如下

var
  x,y,n,m,i,j,q,num,tot,roota,rootb:longint;
  w,u,deep,pre,v,vet,head,lg,next,weight:array[0..200000] of longint;
  st,f:array[0..100100,0..30] of longint;
  fa:array[0..100100,1..2] of longint;
procedure sort(l,r: longint);
var
  i,j,x,y: longint;
begin
  i:=l; j:=r;
  x:=w[(l+r) div 2];
  repeat
    while w[i]<x do inc(i);
    while x<w[j] do dec(j);
    if not(i>j) then
    begin
      y:=w[i]; w[i]:=w[j]; w[j]:=y;
      y:=u[i]; u[i]:=u[j]; u[j]:=y;
      y:=v[i]; v[i]:=v[j]; v[j]:=y;
      inc(i); j:=j-1;
    end;
  until i>j;
  if l<j then sort(l,j);
  if i<r then sort(i,r);
end;
function min(a,b:longint):longint;
begin
  if a>b then exit(b)
  else exit(a);
end;
procedure solve(u,v:longint); inline;
var y,ans,l:longint;
begin
  ans:=maxlongint;
  if deep[u]<deep[v] then 
  begin
    y:=u; u:=v; v:=y;
  end;
  l:=lg[n];
  while deep[u]>deep[v] do 
  begin
    while (deep[f[u][l]]<deep[v]) and (l<>0) do dec(l);
    ans:=min(ans,st[u][l]); 
    u:=f[u][l];
  end;
  if u=v then writeln(ans)
  else
  begin
    l:=lg[n];
    while u<>v do
    begin
      //writeln(u,‘ ‘,v);
      //if (u or v=1) then break;
     
      while (f[u][l]=f[v][l]) and (l<>0) do dec(l);
      ans:=min(ans,st[u][l]); ans:=min(ans,st[v][l]);
      if f[v][l]=0 then begin writeln(v,‘ ‘,l); halt; end;
      u:=f[u][l]; v:=f[v][l];
    end;
    writeln(ans);
  end;
end;
procedure dfs(id,father,we:longint);
var point:longint;
begin
  if father<>id then
  begin
    deep[id]:=deep[father]+1;
    fa[id][1]:=father;
    fa[id][2]:=we;
  end;
  point:=head[id];
  while point<>0 do
  begin
    if vet[point]<>father then dfs(vet[point],id,weight[point]);
    point:=next[point];
  end;
end;
procedure findb(id:longint);
begin
  if pre[id]=id then begin rootb:=id; exit; end
  else findb(pre[id]);
  pre[id]:=rootb;
end;
procedure finda(id:longint);
begin
  if pre[id]=id then begin roota:=id; exit; end
  else finda(pre[id]);
  pre[id]:=roota;
end;
begin
  readln(n,m);
  for i:=1 to n do pre[i]:=i;
  for i:=1 to m do readln(u[i],v[i],w[i]);
  sort(1,m);
  for i:=m downto 1 do
  begin
    finda(u[i]); findb(v[i]);
    if roota<>rootb then
    begin
      pre[roota]:=rootb;
      inc(num); inc(tot);
      next[tot]:=head[u[i]]; head[u[i]]:=tot;
      vet[tot]:=v[i]; weight[tot]:=w[i];
      inc(tot);
      next[tot]:=head[v[i]]; head[v[i]]:=tot;
      vet[tot]:=u[i]; weight[tot]:=w[i];
    end;
    if num=n-1 then break;
  end;
  lg[1]:=0;
  for i:=2 to n do lg[i]:=lg[i>>1]+1;
  for i:=1 to n do 
  if fa[i][1]=0 then 
  begin 
    fa[i][1]:=i; 
    deep[i]:=1; 
    dfs(i,i,0); 
  end;
  for i:=1 to n do
  begin
    f[i][0]:=fa[i][1];
    st[i][0]:=fa[i][2];
  end;
  for j:=1 to lg[n] do
  begin
    for i:=1 to n do
    begin
      f[i][j]:=f[f[i][j-1]][j-1];
      if st[i][j-1]=0 then st[i][j-1]:=maxlongint;
      if st[f[i][j-1]][j-1]=0 then st[f[i][j-1]][j-1]:=maxlongint;
      st[i][j]:=min(st[i][j-1],st[f[i][j-1]][j-1]);
    end;
  end;
  readln(q);
  for i:=1 to q do
  begin
    readln(x,y);
    finda(x); findb(y);
    if roota=rootb then solve(x,y)
    else writeln(-1);
  end;
end.

  

以上是关于树上倍增——(货车运输 解题报告)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P3379lca,HDU2586,洛谷P1967货车运输,倍增lca,树上倍增

Luogu P1967 货车运输题解

P1967 货车运输最大生成树+倍增LCA!!!

洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA

洛谷P1967货车运输

luogu1967noip2013 货车运输 [生成树kruskal LCA ]