1. 程式人生 > >[NOI2018 歸程] 克魯斯卡爾重構樹 Dijkstra最短路 倍增lca

[NOI2018 歸程] 克魯斯卡爾重構樹 Dijkstra最短路 倍增lca

想看可持久化並查集戳我
這次NOI2018的D1炸成翔了 只有13分…. 網路賽選手怕是可以退役了qaq
T1似乎全場簽到題 隨隨便便切 考場上只想出來了55分的最短路加樹的倍增做法
然後我們來看看正解應該怎麼寫
首先你要知道一個叫克魯斯卡爾重構樹的東西, 看我的上上篇轉載的部落格有很詳細的介紹
這裡寫圖片描述
對於此題我們構建出原圖海拔的最大生成樹 每條邊加入時新建一個點作為其連線的兩個聯通塊中的根節點的父親 並把權值留在這個新點上
照這個思路構出克魯斯卡爾重構樹後我們發現對於生成樹中任意一個點x, 只要能到達點x, 那麼也可以到達x子樹中任意一點 那麼這樣我們只需要預處理一號點與各個點之間的最短路 查詢的時候倍增就好啦

題目連結

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define FOR(i, a, b) for(register int i = a; i >= b; -- i)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define PLI pair<ll, int> #define mp make_pair using namespace std; typedef long long ll; const int maxn = 3e6 + 10; int to[maxn], head[maxn], nxt[maxn], a[maxn], l[maxn], e; int fa[maxn][22], dep[maxn], f[maxn], root; ll dis[maxn], lastans, len[maxn]; int T, n, m, Q, S, K; struct edge { int
x, y, L, A; }E[maxn]; template<class T>inline bool chkmin(T &_, T __) { return _ > __ ? _ = __, 1 : 0; } template<class T>inline void read(T &_) { T ___ = 1, __ = getchar(); for(; !isdigit(__); __ = getchar()) if(__ == '-') ___ = -1; for(_ = 0; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48); _ *= ___; } bool cmp(edge A, edge B) { return A.A > B.A; } int find(int x) { return f[x] = x == f[x] ? x : find(f[x]); } void add(int x, int y, int L, int A) { to[++ e] = y; nxt[e] = head[x]; head[x] = e; l[e] = L; a[e] = A; } void Dijkstra() { __gnu_pbds::priority_queue<PLI, greater<PLI>, __gnu_pbds::pairing_heap_tag> q; For(i, 1, n) dis[i] = 1e18; q.push(mp(dis[1] = 0, 1)); while(!q.empty()) { PLI k = q.top(); q.pop(); if(k.first > dis[k.second]) continue; go(k.second, i) if(chkmin(dis[to[i]], dis[k.second] + l[i])) q.push(mp(dis[to[i]], to[i])); } } void dfs(int x) { len[x] = 1e18; if(1 <= x && x <= n) len[x] = dis[x]; go(x, i) if(!dep[to[i]]) { dep[to[i]] = dep[x] + 1; fa[to[i]][0] = x; dfs(to[i]); chkmin(len[x], len[to[i]]); } } ll query(int x, int y) { FOR(j, 21, 0) if(fa[x][j] != 0 && fa[x][j] > n && E[fa[x][j] - n].A > y) x = fa[x][j]; return len[x]; } void Get_ans() { int x, y; e = 0; For(i, 1, n + m) f[i] = i, dep[i] = 0, head[i] = 0; sort(E + 1, E + m + 1, cmp); For(i, 1, m) { int u = find(E[i].x), v = find(E[i].y); if(u != v) { f[u] = f[v] = f[i + n]; add(u, i + n, 0, 0), add(i + n, u, 0, 0); add(v, i + n, 0, 0), add(i + n, v, 0, 0); root = i + n; } } dep[root] = 1, dfs(root); For(j, 1, 21) For(i, 1, n + m) fa[i][j] = fa[fa[i][j - 1]][j - 1]; read(Q), read(K), read(S); while(Q --) { read(x), read(y); x = (lastans * K + x - 1) % n + 1; y = (lastans * K + y) % (S + 1); printf("%lld\n", lastans = query(x, y)); } } int main() { #ifndef ONLINE_JUDGE freopen("return.in", "r", stdin); freopen("return.out", "w", stdout); #endif int x, y, L, A; for(read(T); T -- ; ) { lastans = 0; read(n), read(m); For(i, 1, m) { read(x), read(y), read(L), read(A); add(x, y, L, A), add(y, x, L, A); E[i] = (edge){x, y, L, A}; } Dijkstra(); Get_ans(); For(i, 1, n + m) head[i] = 0; e = 0; } return 0; }