[算法学习] 线段树优化建图
线段树优化建图
有一种最短路问题,有的边是从区间到区间的,这个时候点操作就不是很好办(边比较多)数量级是\(O(n^2)\)。于是考虑建线段树优化建图。
两颗线段树:入与出。(出表示从这里出发,入表示进到了这个点)
线段树优化连边,利用到线段树的思想.
对于每个区间,新建一个节点,向子区间递归连边.
这样,当连向一段区间,就等于连向了所有其子节点
建两棵线段树,第一棵只连自上而下的边,第二棵只连自下而上的边。
然后一个点对另一个区间连边的数量级就降成了\(O(\log n)\)
总复杂度就是\(O(n \log n)\)
给出模板题代码
(区间向点连边,点向区间连边)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
typedef long long ll;
const ll INF = (1ll << 62) - 1;
#define DEBUG(x) std::cerr << #x << " = " << x << std::endl
template <typename T> void read (T &x) {
x = 0; bool f = 1; char ch;
do {ch = getchar(); if (ch == '-') f = 0;} while (ch > '9' || ch < '0');
do {x = x * 10 + ch - '0'; ch = getchar();} while (ch >= '0' && ch <= '9');
x = f ? x : -x;
}
template <typename T> void write (T x) {
if (x < 0) x = ~x + 1, putchar ('-');
if (x > 9) write (x / 10);
putchar (x % 10 + '0');
}
#define int ll
const int N = 1e6 + 7;
struct EDGE {
int w, to, nxt;
} edge[N << 2];
struct Node {
int u, d;
bool operator < (const Node&rhs) const {
return d > rhs.d;
}
};
struct Segment_Tree {
int l, r, ls, rs;
} t[N << 2];
#define ls(p) (t[p].ls)
#define rs(p) (t[p].rs)
priority_queue < Node > Q;
int n, q, s, E, cnt, in_rt, out_rt, in[N], out[N], dis[N], head[N];
inline void addedge(int u, int v, int w) {
edge[++E].to = v;
edge[E].nxt = head[u];
edge[E].w = w;
head[u] = E;
}
inline void build_in(int &p, int l, int r) {
p = ++cnt; t[p].l = l; t[p].r = r;
if(l == r) {
in[l] = p;
return;
}
int mid = (t[p].l + t[p].r) >> 1;
build_in(ls(p), l, mid);
build_in(rs(p), mid + 1, r);
addedge(ls(p), p, 0); addedge(rs(p), p, 0);
}
inline void build_out(int &p, int l, int r) {
p = ++cnt; t[p].l = l; t[p].r = r;
if(l == r) {
out[l] = p;
return;
}
int mid = (t[p].l + t[p].r) >> 1;
build_out(ls(p), l, mid);
build_out(rs(p), mid + 1, r);
addedge(p, ls(p), 0); addedge(p, rs(p), 0);
}
inline void modify_in(int p, int x, int l, int r, int w) {
if(l <= t[p].l && t[p].r <= r) {
addedge(p, x, w);
return;
}
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) modify_in(ls(p), x, l, r, w);
if(r > mid) modify_in(rs(p), x, l, r, w);
}
inline void modify_out(int p, int x, int l, int r, int w) {
if(l <= t[p].l && t[p].r <= r) {
addedge(x, p, w);
return;
}
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) modify_out(ls(p), x, l, r, w);
if(r > mid) modify_out(rs(p), x, l, r, w);
}
inline void dijkstra() {
for(int i = 1; i <= cnt; i++) dis[i] = INF;
Q.push((Node){in[s], 0}); dis[in[s]] = 0;
while(!Q.empty()) {
Node t = Q.top(); Q.pop();
int u = t.u, d = t.d;
if(dis[u] != d) continue;
for(int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to, w = edge[i].w;
if(dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
Q.push((Node){v, dis[v]});
}
}
}
}
signed main() {
read(n); read(q); read(s);
build_in(in_rt, 1, n);
build_out(out_rt, 1, n);
for(int i = 1; i <= n; i++) addedge(in[i], out[i], 0), addedge(out[i], in[i], 0);
for(int i = 1, opt, v, u, l, r, w; i <= q; i++) {
read(opt);
if(opt == 1) {
read(u); read(v); read(w);
addedge(in[u], out[v], w);
}
if(opt == 2) {
read(u); read(l); read(r); read(w);
modify_out(out_rt, in[u], l, r, w);
}
if(opt == 3) {
read(u); read(l); read(r); read(w);
modify_in(in_rt, out[u], l, r, w);
}
}
dijkstra();
for(int i = 1; i <= n; i++) {
if(dis[out[i]] != INF) printf("%lld ", dis[out[i]]);
else printf("-1 ");
}
return 0;
}