[算法学习] 线段树优化建图

线段树优化建图

有一种最短路问题,有的边是从区间到区间的,这个时候点操作就不是很好办(边比较多)数量级是\(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;
}
posted @ 2020-09-21 23:21  Hock  阅读(198)  评论(0编辑  收藏  举报