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

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

首先倍增就不说了

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

其实没什么区别,对于树上的结点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

时间: 2024-11-09 19:23:40

树上倍增——(货车运输 解题报告)的相关文章

51nod———货物运输 解题报告

公元2222年,l国发生了一场战争. 小Y负责领导工人运输物资. 其中有m种物资的运输方案,每种运输方案形如li,ri.表示存在一种货物从li运到ri. 这里有n个城市,第i个城市与第i+1个城市相连(这里1号城市和n号城市并不相连),并且从i号城市走到i+1号或者从i+1号走到i号需要耗费1点时间. 由于高科技的存在,小Y想到了一种节省时间的好方案.在X号城市与Y号城市之间设立传送站,只要这么做,在X号城市走到Y号城市不需要耗费时间,同样的,从Y号城市走到X号城市也不需要耗费时间. 但是为了防

poj1330|bzoj3732|noip2013 货车运输 kruskal+倍增lca

学了一早上倍增,感觉lca还是tarjan好写. poj1330 1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #include <algorithm> 5 #define DEG 20//2^20 6 #define maxn 10010 7 using namespace std; 8 struct node 9 { 10 int v, next; 11 }a[maxn*2

倍增LCA NOIP2013 货车运输

货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条

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

这题的题意是:对于每组x.y,求x到y路径上最小边权的最大值.于是可以使用最大生成树,因为最大生成树满足性质:生成树中最小边权最大,且任意两点间路径上最小边权最大.有了树之后,要求路径,那就要考虑LCA.首先,这题可以树剖,但是我太懒了,于是写了倍增233具体搞法:Kruskal跑出最大生成树,然后在树上倍增LCA,处理出2个数组:fa[][]和minv[][]:fa[][]表示第2^k个父亲是谁,minv[][]表示当前节点到其第2^k个父亲的路径上的最小边权.对于每次查询,在求LCA的同时,

NOIp 2013 Day1 解题报告

NOIp 2013 Day1 解题报告 1.   转圈游戏 不难看出答案就是(x+m*10k) mod n 用快速幂算法,复杂度O(log2k) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,m,x,

货车运输

版权声明:本文为博主原创文章,未经博主允许不得转载. 传送门:货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个

洛谷P1967 [NOIP2013提高组Day1T2]货车运输

P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y

C++之路进阶——LCA(货车运输)

3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入描述 Input Description 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和

刷题总结——货车运输

题目: 题目背景 NOIP2013 提高组 Day1 试题. 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入格式 第一行有两个用一个空格隔开的整数 n ,m,表示 A 国有 n 座城市和 m 条道路. 接下来 m 行每行 3 个整数 x.y.z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z