[bzoj4699][Dijkstra][线段树][并查集]树上的最短路

39 篇文章 0 订阅
20 篇文章 0 订阅

Description

下水道的主干路由n个节点和n-l条边组成,每条边有一个通过它所需的时间Ti。换言之,这是一棵n个节点的带权
树。现在,要用最快的速度赶往目标节点k。下水道有一些塌陷,这导致主干路的某一段路径可以通过该塌陷到另
一条路径。对于一个塌陷,我们用(L1,ri,L2,R2,c)来描述,即对于主干路上L1到R1路径上的任意节点x,L2到
r2路径上的任意节点y,都可以在c的时间内从x走到y。因为不知道自己所在的到底是哪个节点,所以要求出每个节
点到目标节点K的最短距离。注意边是单向的

Input

第一行两个数n,m,k,表示节点数、塌陷数和目标节点编号,空格分隔。
接下来n-l行,每行3个数x,y,t,表示主干路的一条边连接点x,y,通过的时间为t。
接下来m行,每行5个数L1,r1,L2,r2,c,表示一个塌陷。 N<=250000 m<=100000 1 < = L1,L2,
R1, R2,x,y < = N 1< = t,c< = 2^31-1。

Output

n行,每行一个数,表示第i个节点到第k个节点的最短路径长度。 特别的,在第k行你应当输出0。

Sample Input

5 3 5

1 2 100

2 3 100

3 4 100

4 5 100

1 2 4 5 200

2 2 4 4 90

3 3 2 2 5

Sample Output

200

190

195

100

0

题解

由于博主老年码不动了…
所以这篇博客是个口胡题解
有需要可以叫博主咕咕
我们先考虑一个性质,一个塌陷仅仅会被扩展一次
再考虑dij的过程,先被拿出来的点一定是最优的
于是我们先考虑拿出来点 x x x
设其子树DFS序区间为 [ i n x , o t x ] [in_x,ot_x] [inx,otx]
覆盖了他的塌陷即为右端点在 [ i n x , o t x ] [in_x,ot_x] [inx,otx],左端点在之前的。或者左端点在 [ i n x , o t x ] [in_x,ot_x] [inx,otx],右端点在之后的。开两棵线段树维护,以维护右端点的线段树为例。每个节点维护满足右端点在 [ L , R ] [L,R] [L,R]的塌陷编号,其中按左端点升序,维护左端点同理
我们发现,每一个点将其线段树上 [ i n x , o t x ] [in_x,ot_x] [inx,otx]分割成的log个区间的合法塌陷拿出来,以右端点为例,不难发现其实等价于删除线段树上维护的区间前面的连续一段,左端点同理
所以每个塌陷会被拿出来 l o g n logn logn次,维护一个标记表示该塌陷是否被扩展。则每条塌陷仅会被扩展一次
每次扩展我们往堆中扔两个元素 [ l , L C A , d i s ] [l,LCA,dis] [l,LCA,dis] [ r , L C A , d i s ] [r,LCA,dis] [r,LCA,dis]
表示其对应塌陷的路径,这一整段都可以以 d i s dis dis的代价访问
当一个点扩展完之后,将其与其父亲用并查集合并起来,并查集的每个联通块表示除了其根以外其他点均被扩展过
当从堆中取出一个正常点时,正常扩展
当从堆中取出一个塌陷带来的点时,从这条链底部暴力往上跳并查集的根节点,对其执行扩展,之后将其与其父亲合并。一个并查集的根节点显然代表了之后在堆中被取出不会更优的点
每个塌陷会带来 2 2 2个点,每个塌陷仅会被扩展一次,每个点至多会扩展一次,故复杂度为优越的 ( m + n ) l o g n (m+n)logn (m+n)logn

代码咕咕咕

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值