线段树优化建图

有一种最短路问题,有的边是从区间到区间的,这个时候点操作就不是很好办(边比较多)。于是考虑建线段树优化建图。

两颗线段树:入与出。(出表示从这里出发,入表示进到了这个点)

于是每条区间加边就可以转为log级别。

1、平行节点间,从入连向出一条0边(进入了这个点,当然可以从这个点出发)。

2、入线段树中,每个点都像左右儿子区间连0边(进入了这个区间,也可以认为进入了区间的子集)。

3、出线段树每个点都像父亲连0边(从这个区间可以出发,也可以从这个区间的父区间出发。这里要注意,到了3~5这个区间并不代表可以下一步走3发的边。)

4、每条题目给的边都从出连向入。

cf787d:

#include 
#include 
#include 
#include 
#define N 200010
using namespace std;
typedef long long ll;
const ll inf = 200000000000000ll;
struct segt {int ls, rs;}t[N<<2];
struct edge {int fr, to, val, next;}e[N*20];
int n, m, s, segcnt = 0, head[N<<2], cnt = 0, belong[N];
inline void ins(int x, int y, int z) {e[++cnt].to = y; e[cnt].fr = x; e[cnt].val = z; e[cnt].next = head[x]; head[x] = cnt;}
void build(int p, int l, int r) {
	if(l == r) {belong[l] = p; return ;}
	int mid = (l + r)>>1, now = p;
	build((t[now].ls = ++segcnt), l, mid);
	build((t[now].rs = ++segcnt), mid + 1, r);
	ins(now, t[now].ls, 0); ins(now, t[now].rs, 0);
}
int sta[N<<2][2], stp[2];
void Find(int p, int l, int r, int x, int y, int k) {
	if(x <= l && r <= y) {sta[++stp[k]][k] = p; return ;}
	int mid = (l + r)>>1;
	if(x <= mid) Find(t[p].ls, l, mid, x, y, k);
	if(mid + 1 <= y) Find(t[p].rs, mid + 1, r, x, y, k);
}
ll dis[N<<2]; int v[N<<2]; deque Q;
inline void spfa() {
	s = belong[s] - segcnt;
	for(int i = 0; i < N<<2; ++i) dis[i] = inf; dis[s] = 0;
	memset(v, 0, sizeof(v)); Q.push_back(s); v[s] = 1;
	while(!Q.empty()) {
		int now = Q.front(); Q.pop_front(); v[now] = 0;
		for(int i = head[now]; i; i = e[i].next)
			if(dis[e[i].to] > dis[now] + e[i].val) {
				dis[e[i].to] = dis[now] + e[i].val;
				if(!v[e[i].to]) {
					v[e[i].to] = 1;
					if(dis[e[i].to] < dis[(!Q.empty())?Q.front():0]) Q.push_front(e[i].to);
					else Q.push_back(e[i].to);
				}
			}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &s);
	build(++segcnt, 1, n);
	int temp = cnt;
	for(int i = 1; i <= temp; ++i) ins(e[i].to + segcnt, e[i].fr + segcnt, 0);
	for(int i = 1; i <= segcnt; ++i) ins(i, i + segcnt, 0);
	for(int i = 1; i <= n; ++i) belong[i]+= segcnt;
	for(int i = 1; i <= m; ++i) {
		int opt, v, l, r, w; scanf("%d%d%d", &opt, &v, &l);
		if(opt != 1) scanf("%d", &r); else r = l; scanf("%d", &w);
		stp[0] = stp[1] = 0; if(opt != 3) {Find(1, 1, n, v, v, 0); Find(1, 1, n, l, r, 1);} else {Find(1, 1, n, l, r, 0); Find(1, 1, n, v, v, 1);}
		for(int i1 = 1; i1 <= stp[0]; ++i1)
			for(int i2 = 1; i2 <= stp[1]; ++i2) ins(sta[i1][0] + segcnt, sta[i2][1], w);
	}
	spfa();
	for(int i = 1; i <= n; ++i) if(dis[belong[i]] == inf) printf("-1 "); else printf("%I64d ", dis[belong[i]]);
	return 0;
}

你可能感兴趣的:(数据结构,图论)