NOI2018 歸程 kruskal重構樹

2022-04-29 22:57:16 字數 1533 閱讀 8111

lg傳送門

kruskal重構樹模板題。

另一篇文章裡有關於kruskal重構樹更詳細的介紹和更板子的題目。

題意懶得說了,這題的關鍵在於快速找出從查詢的點出發能到達的點(即經過海拔高於水位線的邊能到達的點)中距離\(1\)號點最近的距離。

看上去可以kruskal,假設我們把邊實現按海拔從大到小排序,考慮我們的重構樹的性質:乙個小根堆,任意乙個點到根節點的路徑上的點權單調不公升,且這條路徑上最淺的高於水位線的點\(u\)的子樹中的所有葉節點就是這個點所能到達的所有點。dijkstra預處理每個點到\(1\)的最短路,再在重構樹上一遍dfs處理出每棵子樹中到\(1\)的最短距離,查詢時\(u\)直接倍增求,這題就做完了。

#include #include #include #include #include #define r register

#define i inline

#define b 1000000

using namespace std;

const int n = 200003, m = 400003, inf = 2e9;

char buf[b], *p1, *p2;

i char gc()

i int rd()

int s[n], vis[n], dis[n], f[m], o[m], val[m], son[m][2], dep[m], fa[m][20], n, cnt;

struct edge e[m];

vector > g[n];

priority_queue > q;

i int operator < (edge x, edge y)

i int min(int x, int y)

i int find(int x)

void dfs(int x, int f)

dfs(son[x][0], x), dfs(son[x][1], x), o[x] = min(o[son[x][0]], o[son[x][1]]);

}i int query(int x, int y)

int main() ;

g[x].push_back(make_pair(y, z)), g[y].push_back(make_pair(x, z));

}for (i = 1; i <= n; ++i)

s[i] = g[i].size(), dis[i] = inf;

for (i = 1; i < n << 1; ++i)

f[i] = i;

dis[1] = 0, q.push(make_pair(0, 1));

while (!q.empty())

}sort(e + 1, e + m + 1), cnt = n;

for (i = 1; i <= m; ++i)

}dfs(cnt, 0), q = rd(), k = rd(), s = rd();

for (i = 1; i <= q; ++i)

}return 0;

}

NOI2018 歸程 題解

容易發現,當水位線確定了,圖會被分成若干個連通塊,連通塊裡的點可以互相到達,故連通塊內點的答案為其中的點到 1 的距離的最小值,這個可以先 dijkstra 預處理求出 spfa 死了 那考慮怎麼維護連通塊。可持久化並查集?感覺不好搞,有一種神奇的演算法 kruscal 重構樹。其實就是 krusc...

NOI 2018 歸程 Kruskal重構樹

題目大意 太長了,略 kruskal重構樹,很神奇的乙個演算法吧 如果兩個並查集被某種條件合併,那麼這個條件作為乙個新的節點連線兩個並查集 那麼在接下來的提問中,如果某個點合法,它的所有子節點也都合法,即子節點的限制少於父節點 include include include include defi...

NOI2018 歸程(kruskal重構樹)

思路 每天的最短距離相當於每天走高於海拔高於水位線的路所能到達的所有點中到1的最短距離。現在要想辦法能夠直接查詢這個最短距離,如果能根據海拔構造一種樹狀結構,那麼每次能開車到達的點對應一顆子樹,那麼只需要倍增出這顆子樹的根節點就可以了。kruskal重構樹 對海拔從大到小排序,然後跑kruskal,...