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