倍增新高度——树上倍增(其实差不多啦)
首先倍增就不说了
那么树上倍增和倍增有什么区别呢?
其实没什么区别,对于树上的结点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。
对于 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.
原文:https://www.cnblogs.com/by-w/p/9675386.html