線段樹優化建圖學習筆記
阿新 • • 發佈:2021-08-17
線段樹優化建圖適用於對一段區間內的點進行連邊,如果暴力連邊,複雜度是 \(O(n^2m)\) 的,顯然過大。
考慮支援各種區間操作的線段樹。建立兩棵線段樹,第一棵樹從父親向兒子連邊權為0的邊,這裡稱為出樹,第二棵樹從兒子向父親連邊權為0的邊這裡稱為入樹,它們的葉子節點是相同的,為題目裡給出的 \(1 - n\) ,這樣區間連邊就可以連到父親上了。
題意:
\([a,b]\) 內的點向 \([c,d]\) 內的點連邊,邊權為1。
求 \(s\) 到 \(1-n\) 的最短路。
連邊是新開兩個節點 \(u\) 和 \(v\),出樹裡 \([a,b]\)
然後跑最短路即可。
#include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstring> using namespace std; typedef pair<int, int> P; const int N = 2e6 + 5; int rd() { int x = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) x = x * 10 + c - '0', c = getchar(); return x; } int rt1, rt2, cnt, lc[N], rc[N]; vector <P> G[N]; void add(int u, int v, int w) { G[u].push_back(P(v, w)); } void build(int &p1, int &p2, int l, int r) { if(l == r) { p1 = p2 = l; return; } p1 = ++cnt, p2 = ++cnt; int mid = (l + r) >> 1; build(lc[p1], lc[p2], l, mid); add(p1, lc[p1], 0), add(lc[p2], p2, 0); build(rc[p1], rc[p2], mid + 1, r); add(p1, rc[p1], 0), add(rc[p2], p2, 0); } void update(int p, int l, int r, int L, int R, int u, int op) { if(L <= l && r <= R) { if(op) add(u, p, 0); else add(p, u, 0); return; } int mid = (l + r) >> 1; if(L <= mid) update(lc[p], l, mid, L, R, u, op); if(R > mid) update(rc[p], mid + 1, r, L, R, u, op); } int dis[N]; priority_queue <P, vector<P>, greater<P> > que; void dijkstra(int s) { memset(dis, 0x3f, sizeof(dis)); dis[s] = 0; que.push(P(0, s)); while(!que.empty()) { P p = que.top(); que.pop(); int u = p.second; if(dis[u] < p.first) continue; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i].first, w = G[u][i].second; if(dis[u] + w < dis[v]) { dis[v] = dis[u] + w; que.push(P(dis[v], v)); } } } } int main() { int n = rd(), m = rd(), s = rd(); cnt = n; build(rt1, rt2, 1, n); while(m--) { int a = rd(), b = rd(), c = rd(), d = rd(), u, v; u = ++cnt, v = ++cnt; add(v, u, 1); update(rt1, 1, n, a, b, u, 1); update(rt2, 1, n, c, d, v, 0); u = ++cnt, v = ++cnt; add(v, u, 1); update(rt1, 1, n, c, d, u, 1); update(rt2, 1, n, a, b, v, 0); } dijkstra(s); for(int i = 1; i <= n; i++) printf("%d\n", dis[i]); return 0; }
題意:
- \(u\) 向 \(v\) 連邊,邊權為 \(w\)。
- \(u\) 向 \([l,r]\) 連邊,邊權為 \(w\)。
- \([l,r]\) 向 \(u\) 連邊,邊權為 \(w\)。
求 \(s\) 到 \(1-n\) 的最短路。
其實是一樣的,就是本題可以直接用 \(u\) 連邊,好寫一些。
#include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstring> #define int long long using namespace std; typedef pair<int, int> P; const int N = 1e6 + 5; int n, m, s; vector <P> G[N]; int cnt, rt1, rt2, ls[N], rs[N]; void buildOUT(int l, int r, int &rt) { if(l == r) { rt = l; return; } rt = ++cnt; int mid = (l + r) >> 1; buildOUT(l, mid, ls[rt]); buildOUT(mid + 1, r, rs[rt]); G[rt].push_back(P(ls[rt], 0)); G[rt].push_back(P(rs[rt], 0)); } void buildIN(int l, int r, int &rt) { if(l == r) { rt = l; return; } rt = ++cnt; int mid = (l + r) >> 1; buildIN(l, mid, ls[rt]); buildIN(mid + 1, r, rs[rt]); G[ls[rt]].push_back(P(rt, 0)); G[rs[rt]].push_back(P(rt, 0)); } void update(int rt, int l, int r, int u, int L, int R, int w, int op) { if(L <= l && r <= R) { if(op == 2) G[u].push_back(P(rt, w)); else G[rt].push_back(P(u, w)); return; } int mid = (l + r) >> 1; if(L <= mid) update(ls[rt], l, mid, u, L, R, w, op); if(R > mid) update(rs[rt], mid + 1, r, u, L, R, w, op); } int dis[N]; priority_queue <P, vector<P>, greater<P> > que; void dijkstra(int s) { for(int i = 1; i <= cnt; i++) dis[i] = 1e18; dis[s] = 0; que.push(P(0, s)); while(!que.empty()) { P p = que.top(); que.pop(); int u = p.second; if(dis[u] < p.first) continue; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i].first, w = G[u][i].second; if(dis[u] + w < dis[v]) { dis[v] = dis[u] + w; que.push(P(dis[v], v)); } } } } int read() { int x = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) x = x * 10 + c - '0', c = getchar(); return x; } signed main() { n = read(), m = read(), s = read(); cnt = n; buildOUT(1, n, rt1), buildIN(1, n, rt2); while(m--) { int op = read(); if(op == 1) { int u = read(), v = read(), w = read(); G[u].push_back(P(v, w)); } else { int u = read(), l = read(), r = read(), w = read(); update(op == 2 ? rt1 : rt2, 1, n, u, l, r, w, op); } } dijkstra(s); for(int i = 1; i <= n; i++) printf("%lld ", dis[i] == 1e18 ? -1 : dis[i]); return 0; }