思路:每天的最短距離相當於每天走高於海拔高於水位線的路所能到達的所有點中到1的最短距離。現在要想辦法能夠直接查詢這個最短距離,如果能根據海拔構造一種樹狀結構,那麼每次能開車到達的點對應一顆子樹,那麼只需要倍增出這顆子樹的根節點就可以了。
kruskal重構樹:對海拔從大到小排序,然後跑kruskal,在求這顆最大生成樹的過程中每次兩個點不在乙個集合,就引入乙個新的結點,作為這兩個點的代表點的父親,點權為邊權,注意在這個過程中要為護重構樹的形態,重構樹具有一下性質:
1.樹上的每條鏈具有單調性(與排序有關);
2原來圖中的結點全部對應重構樹中的葉子結點;
總結:將海拔按照從大到小排序,構造克魯斯卡爾重構樹(注意形態),一次dfs求出以每個結點為根時子樹中的點到1的最短路,對於一組詢問v,p,通過樹上倍增找到這個對應子樹(注意,在水位線為p的前提下,v不可能只走海拔大於p的路徑達到非這顆子樹上的點,一開始我沒注意到這點),得到解。
#include
#include
#include
#include
#include
#include
using
namespace std;
int t,n,m,last[
200005
],cnt,d[
200005
],xx,yy,z,zz,fa[
400005
],num,q,k,s,v0,p0,f[
400005][
25],dep[
400005];
struct edgee[
800005];
struct heapnode};
struct nodeno[
400005];
inline
void
add(
int u,
int v,
int w,
int a)
bool
cmp(edge a,edge b)
intfinds
(int x)
void
dijkstra()
);while
(!q.
empty()
));}
}}}void
kruskal()
}}intdfs
(int u,
int pa)
int minn=
0x3f3f3f3f
; minn=
min(
dfs(no[u]
.l,u)
,minn)
; minn=
min(
dfs(no[u]
.r,u)
,minn)
; no[u]
.md=minn;
return minn;
}int
query
(int x,
int y)
void
solve()
}void
intt()
intmain()
dijkstra()
;for
(int i=
1;i<=
2*n;i++
) fa[i]
=i;scanf
("%d%d%d"
,&q,
&k,&s)
;kruskal()
;solve()
;intt()
;}return0;
}
NOI2018 歸程 題解
容易發現,當水位線確定了,圖會被分成若干個連通塊,連通塊裡的點可以互相到達,故連通塊內點的答案為其中的點到 1 的距離的最小值,這個可以先 dijkstra 預處理求出 spfa 死了 那考慮怎麼維護連通塊。可持久化並查集?感覺不好搞,有一種神奇的演算法 kruscal 重構樹。其實就是 krusc...
NOI 2018 歸程 Kruskal重構樹
題目大意 太長了,略 kruskal重構樹,很神奇的乙個演算法吧 如果兩個並查集被某種條件合併,那麼這個條件作為乙個新的節點連線兩個並查集 那麼在接下來的提問中,如果某個點合法,它的所有子節點也都合法,即子節點的限制少於父節點 include include include include defi...
NOI2018 歸程 kruskal重構樹
lg傳送門 kruskal重構樹模板題。另一篇文章裡有關於kruskal重構樹更詳細的介紹和更板子的題目。題意懶得說了,這題的關鍵在於快速找出從查詢的點出發能到達的點 即經過海拔高於水位線的邊能到達的點 中距離 1 號點最近的距離。看上去可以kruskal,假設我們把邊實現按海拔從大到小排序,考慮我...