CF786B Legacy(线段树优化建图)

嘟嘟嘟


省选Day1T2不仅考了字符串,还考了线段树优化建图。当时不会,现在赶快学一下。


线段树能优化的图就是像这道题一样,一个点像一个区间的点连边,或一个区间像一个点连边。一个个连就是\(O(n ^ 2)\)复杂度了,当然承受不起。于是就有了线段树了。
原理很简单,就是把一个连续区间的点合并成线段树上的一个点,这样最多有\(nlogn\)个点。但仅仅这样还不对,所以我们要建两棵树,一个是入度树,一个是出度树。
对于入度树,每一个点要像左右儿子连边,因为如果这个有人像这个点所代表的区间连边,那么也一定能进入他的左右子区间。
对于出度树,左右儿子向父亲连边,因为如果这个点所代表的区间能走到一个点,则他的父亲也一定能到那个点。
而这种连边反过来就不行。
这样我们线段树内部的连边就搞完了,接下来考虑题中的加边。


对于一个点\(v\)向一个区间连边,就在入度树上区间修改,如果到了递归边界,就从\(v\)向这个点的编号连边;对于区间向点连边同理,这里就不说了。


上面提到点的编号,有一个很好的方法就是线段树的节点从\(n + 1\)开始编号,这样原图上的点的编号不会被打乱,点对点的加边还可以正常加。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const ll INF = 1e18;
const db eps = 1e-8;
const int maxn = 5e6 + 5;
const int maxe = 1e7 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, m, s;
struct Edge
{
	int nxt, to, w;
}e[maxe];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y, int w)
{
	e[++ecnt] = (Edge){head[x], y, w};
	head[x] = ecnt;
}

int tIn[maxn], tOut[maxn], l[maxn], r[maxn], tcnt = 0;
In void build(int now, int L, int R)
{
	l[now] = L; r[now] = R;
	if(L == R)
	{
		tIn[now] = tOut[now] = L;
		return;
	}
	tIn[now] = ++tcnt; tOut[now] = ++tcnt;
	int mid = (L + R) >> 1;
	build(now << 1, L, mid);
	build(now << 1 | 1, mid + 1, R);
	addEdge(tIn[now], tIn[now << 1], 0);
	addEdge(tIn[now], tIn[now << 1 | 1], 0);
	addEdge(tOut[now << 1], tOut[now], 0);
	addEdge(tOut[now << 1 | 1], tOut[now], 0);
}
In void update(int now, int L, int R, int x, int w, bool flg)
{
	if(l[now] == L && r[now] == R)
	{
		if(flg) addEdge(x, tIn[now], w);
		else addEdge(tOut[now], x, w);
		return;
	}
	int mid = (l[now] + r[now]) >> 1;
	if(R <= mid) update(now << 1, L, R, x, w, flg);
	else if(L > mid) update(now << 1 | 1, L, R, x, w, flg);
	else update(now << 1, L, mid, x, w, flg), update(now << 1 | 1, mid + 1, R, x, w, flg);
}

#define pr pair<ll, int>
#define mp make_pair
bool in[maxn];
ll dis[maxn];
In void dijkstra(int s)
{
	fill(dis + 1, dis + tcnt + 1, INF); dis[s] = 0;
	priority_queue<pr, vector<pr>, greater<pr> > q;
	q.push(mp(dis[s], s));
	while(!q.empty())
	{
		int now = q.top().second; q.pop();
		if(in[now]) continue;
		in[now] = 1;
		for(int i = head[now], v; ~i; i = e[i].nxt)
		{
			if(dis[v = e[i].to] > dis[now] + e[i].w)
			{
				dis[v] = dis[now] + e[i].w;
				q.push(mp(dis[v], v));
			}
		}
	}
}

int main()
{
	Mem(head, -1);
	n = read(), m = read(), s = read();
	tcnt = n; build(1, 1, n);
	for(int i = 1; i <= m; ++i)
	{
		int op = read(), x = read();
		if(op == 1)
		{
			int y = read(), w = read();
			addEdge(x, y, w);
		}
		else
		{
			int L = read(), R = read(), w = read();
			update(1, L, R, x, w, op == 2);
		}
	}
	dijkstra(s);
	for(int i = 1; i <= n; ++i) write(dis[i] == INF ? -1 : dis[i]), space; enter;
	return 0;
}
posted @ 2019-04-27 14:44  mrclr  阅读(228)  评论(0编辑  收藏  举报