「学习笔记」线段树优化建图

线段树优化建图适用于对一段区间内的点进行连边,如果暴力连边,复杂度是 \(O(n^2m)\) 的,显然过大。

考虑支持各种区间操作的线段树。建立两棵线段树,第一棵树从父亲向儿子连边权为 0 的边,这里称为入树,第二棵树从儿子向父亲连边权为 0 的边这里称为出树,它们的叶子节点是相同的,为题目里给出的 \(1 - n\) ,这样区间连边就可以连到父亲上了。

例题P6348 [PA2011]Journeys

题意:

\([a,b]\) 内的点向 \([c,d]\) 内的点连边,边权为1。

\(s\)\(1-n\) 的最短路。

连边是新开两个节点 \(u\)\(v\),出树里 \([a,b]\) 所对应的点向 \(v\) 连边,边权为0,\(v\)\(u\) 连边,边权为1,\(u\) 向入树里 \([c,d]\) 所对应的点连边,边权为0。

然后跑最短路即可。

#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;
}

例题CF786B Legacy

题意:

  1. \(u\)\(v\) 连边,边权为 \(w\)
  2. \(u\)\([l,r]\) 连边,边权为 \(w\)
  3. \([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;
}
posted @ 2021-08-17 13:45  Acestar  阅读(118)  评论(0编辑  收藏  举报