樹上倍增——(貨車運輸 解題報告)
倍增新高度——樹上倍增(其實差不多啦)
首先倍增就不說了
那麽樹上倍增和倍增有什麽區別呢?
其實沒什麽區別,對於樹上的結點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,0≤z≤100,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.
樹上倍增——(貨車運輸 解題報告)