1. 程式人生 > >樹上倍增——(貨車運輸 解題報告)

樹上倍增——(貨車運輸 解題報告)

說明 最小生成樹 師說 是我 沖突 假設 讓我 解題報告 lse

倍增新高度——樹上倍增(其實差不多啦)

首先倍增就不說了

那麽樹上倍增和倍增有什麽區別呢?

其實沒什麽區別,對於樹上的結點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.

  

樹上倍增——(貨車運輸 解題報告)