线段树优化建图——CF786B Legacy题解

CF786B Legacy

题面:

题面翻译

Rick 和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此…),与此同时很多坏人正追赶着他们。因此 Rick 想在坏人们捉到他之前把他的遗产留给 Morty。

在宇宙中一共有 n n n 个星球标号为 1 ∼ n 1 \sim n 1n。Rick 现在身处于标号为 s s s 的星球(地球)但是他不知道 Morty 在哪里。

众所周知,Rick 有一个传送枪,他用这把枪可以制造出一个从他所在的星球通往其他星球(也包括自己所在的星球)的单行道路。但是由于他还在用免费版,因此这把枪的使用是有限制的。

默认情况下他不能用这把枪开启任何传送门。在网络上有 q q q 个售卖这些传送枪的使用方案。每一次你想要实施这个方案时你都可以购买它,但是每次购买后只能使用一次。每个方案的购买次数都是无限的。

网络上一共有三种方案可供购买:

  • 开启一扇从星球 v v v 到星球 u u u 的传送门;
  • 开启一扇从星球 v v v 到标号在 [ l , r ] [l,r] [l,r] 区间范围内任何一个星球的传送门。(即这扇传送门可以从一个星球出发通往多个星球)
  • 开启一扇从标号在 [ l , r ] [l,r] [l,r] 区间范围内任何一个星球到星球 v v v 的传送门。(即这扇传送门可以从多个星球出发到达同一个星球)

Rick 并不知道 Morty 在哪儿,但是 Unity 将要通知他 Morty 的具体位置,并且他想要赶快找到通往所有星球的道路各一条并立刻出发。因此对于每一个星球(包括地球本身)他想要知道从地球到那个星球所需的最小钱数。

输入数据:

输入数据的第一行包括三个整数 n n n q q q s s s 分别表示星球的数目,可供购买的方案数目以及地球的标号。

接下来的 q q q 行表示 q q q 种方案。

  • 输入 1 v u w 表示第一种方案,其中 v , u v,u v,u 意思同上, w w w 表示此方案价格。
  • 输入 2 v l r w 表示第二种方案,其中 v , l , r v,l,r v,l,r 意思同上, w w w 表示此方案价格。
  • 输入 3 v l r w 表示第三种方案,其中 v , l , r v,l,r v,l,r 意思同上, w w w 表示此方案价格。
输出格式:

输出一行用空格隔开的 n n n 个整数分别表示从地球到第 i i i 个星球所需的最小钱数。如果不能到达那个星球,输出-1。

说明:
在第一组测试样例里,Rick 可以先购买第 4 4 4 个方案再购买第 2 2 2 个方案从而到达标号为 2 2 2 的星球.

【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ n , q ≤ 1 0 5 1\le n,q \le 10^5 1n,q105 1 ≤ w ≤ 1 0 9 1\le w \le 10^9 1w109

题目描述

Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them.

There are $ n $ planets in their universe numbered from $ 1 $ to $ n $ . Rick is in planet number $ s $ (the earth) and he doesn’t know where Morty is. As we all know, Rick owns a portal gun. With this gun he can open one-way portal from a planet he is in to any other planet (including that planet). But there are limits on this gun because he’s still using its free trial.

By default he can not open any portal by this gun. There are $ q $ plans in the website that sells these guns. Every time you purchase a plan you can only use it once but you can purchase it again if you want to use it more.

Plans on the website have three types:

  1. With a plan of this type you can open a portal from planet $ v $ to planet $ u $ .
  2. With a plan of this type you can open a portal from planet $ v $ to any planet with index in range $ [l,r] $ .
  3. With a plan of this type you can open a portal from any planet with index in range $ [l,r] $ to planet $ v $ .

Rick doesn’t known where Morty is, but Unity is going to inform him and he wants to be prepared for when he finds and start his journey immediately. So for each planet (including earth itself) he wants to know the minimum amount of money he needs to get from earth to that planet.

输入格式

The first line of input contains three integers $ n $ , $ q $ and $ s $ ( $ 1<=n,q<=10^{5} $ , $ 1<=s<=n $ ) — number of planets, number of plans and index of earth respectively.

The next $ q $ lines contain the plans. Each line starts with a number $ t $ , type of that plan ( $ 1<=t<=3 $ ). If $ t=1 $ then >it is followed by three integers $ v $ , $ u $ and $ w $ where $ w $ is the cost of that plan ( $ 1<=v,u<=n $ , $ 1<=w<=10^{9} $ ). Otherwise it is followed by four integers $ v $ , $ l $ , $ r $ and $ w $ where $ w $ is the cost of that plan ( $ 1<=v<=n $ , $ 1<=l<=r<=n $ , $ 1<=w<=10^{9} $ ).

输出格式

In the first and only line of output print $ n $ integers separated by spaces. $ i $ -th of them should be minimum money to get >from earth to $ i $ -th planet, or $ -1 $ if it’s impossible to get to that planet.

样例 #1
样例输入 #1
3 5 1
2 3 2 3 17
2 3 2 2 16
2 2 2 3 3
3 3 1 1 12
1 3 3 17
样例输出 #1
0 28 12
样例 #2
样例输入 #2
4 3 1
3 4 1 3 12
2 2 3 4 10
1 2 4 16
样例输出 #2
0 -1 -1 12
提示

In the first sample testcase, Rick can purchase $ 4 $ th plan once and then $ 2 $ nd plan in order to get to get to planet number $ 2 $ .

线段树优化建图的典典典典题

我们苦恼:

  1. 如何让一个点连向一个区间内所有的点
  2. 如何让一个区间内所有的点连向一个点

区间,嗯?区间,嗯!

解决区间!去吧!线段树,就决定是你了!

*线段树使用了分治


先来思考如何让一个点只有一条出边,却能通向多个点

地球人都知道,给这多个点(特指目标点)建个超级源点嘛!

让这个点和超级源点相连就好了!

所以,我们利用线段树上的节点做超级源点

那么,我们要连一个区间的点,原需要 O ( n ) \mathcal{O}(n) O(n) 次连边,现只需要 O ( log ⁡ n ) \mathcal{O}(\log n) O(logn) 次连边。

但是,光一个线段树肯定不行

这题是建有向边,能卖你无向图?迪杰斯特拉的时候,点不得到处跑

自然,线段树上也应该有方向!


这个博主的 blog 给了我很大帮助!感谢

我们先考虑点连区间

上图!
img

我称之为分散树( s e g O U T \color{blue}{segOUT} segOUT)

我们将一个点 u u u 连上这个区间 [ l , r ] [l,r] [l,r] 的源点,

这个点 u u u 自然可以有路径指向 [ l , r ] [l,r] [l,r] 中每个点


然后是区间连点

上图!
img

我称之为汇聚树( s e g I N \color{red}{segIN} segIN)

我们将这个区间 [ l , r ] [l,r] [l,r] 汇聚到源点,然后这个源点指向了 u u u [ l , r ] [l,r] [l,r] 的每个点都有路径指向 u u u


!!注意Tip:

  1. 因为是建图,所以线段树上的叶子节点就是对应指向的节点(很重要,要拿来分析空间!)
  2. 线段树上每一条边,边权都是 0 0 0(Zero would be nice) (因为方案 2 , 3 2,3 2,3 是购买一次可以解决 n n n 条线路国内充值VIP现状

时间复杂度就不分析了,因为主要问题在于减少暴力连边次数,
我们已经解决了!

那么重头戏!空间复杂度分析

首先,我们建了多少点?

我们可以按满二叉树算,那么一个线段树我们建的源点有 O ( N − 1 ) \mathcal{O}(N - 1) O(N1) 个(不会的烦请自行百度)

我们开了两个线段树,建了 O ( 2 N − 2 ) \mathcal{O}(2N - 2) O(2N2)

那么总开点数为 O ( 3 N ) \mathcal{O}(3N) O(3N)

因为每次操作只会连 O ( log ⁡ n ) \mathcal{O}(\log n) O(logn) 条边,线段树上还有 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn) 条边,

所以共需要存约 O ( q log ⁡ n + 2 n log ⁡ n ) \mathcal{O}(q\log n + 2n\log n) O(qlogn+2nlogn) 条边

这样本题就彻底结束了!

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int N = 1e5+5,M = 3e5+5;
int n,q,s;
int head[M],to[N * 20],nxt[N * 20],val[N * 20],cnt,po;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v,int w) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	val[cnt] = w;
	head[u] = cnt++;
}
#define mid ((pl + pr) >> 1)
int ls[M * 20],rs[M * 20];
namespace segIN{
int rt;
void build(int &p,int pl,int pr){
	if(pl == pr) {
		p = pl;
		return;
	}
	p = ++po;
	build(ls[p],pl,mid);
	build(rs[p],mid+1,pr);
	add(ls[p],p,0);
	add(rs[p],p,0);
}

void update(int p,int pl,int pr,int f,int v,int l,int r) {
	if(l <= pl && pr <= r) {
		add(p,f,v);
		return;
	}
	if(l <= mid) update(ls[p],pl,mid,f,v,l,r);
	if(r > mid) update(rs[p],mid+1,pr,f,v,l,r);
}
}

namespace segOUT{
int rt;
void build(int &p,int pl,int pr) {
	if(pl == pr) {
		p = pl;
		return;
	}
	p = ++po;
	build(ls[p],pl,mid);
	build(rs[p],mid+1,pr);
	add(p,ls[p],0);
	add(p,rs[p],0);
}
void update(int p,int pl,int pr,int f,int v,int l,int r) {
	if(l <= pl && pr <= r) {
		add(f,p,v);
		return;
	}
	if(l <= mid) update(ls[p],pl,mid,f,v,l,r);
	if(r > mid) update(rs[p],mid+1,pr,f,v,l,r);
}
}

#undef mid

void SigToSig(){
	int u = rd(),v = rd(),w = rd();
	add(u,v,w);
}

void SigToRan() {
	int v = rd(),l = rd(),r = rd(),w = rd();
	segOUT::update(segOUT::rt,1,n,v,w,l,r);
}

void RanToSig() {
	int v = rd(),l = rd(),r = rd(),w = rd();
	segIN::update(segIN::rt,1,n,v,w,l,r);
}

const int inf = 0x3f3f3f3f3f3f3f3fLL;
int dis[M];
bool vis[M];
priority_queue<array<int,2>,vector<array<int,2>>,greater<array<int,2>>> Q;

void dij(int s) {
	memset(dis,0x3f,sizeof(dis));
	dis[s] = 0;
	Q.emplace(array<int,2>{0,s});
	while(Q.size()) {
		array<int,2> t = Q.top();
		Q.pop();
		if(vis[t[1]]) continue;
		vis[t[1]] = true;
		for(int i = head[t[1]];~i;i = nxt[i]) {
			int y = to[i],w = val[i];
			if(dis[y] > dis[t[1]] + w) {
				dis[y] = dis[t[1]] + w;
				Q.emplace(array<int,2>{dis[y],y});
			}
		}
	}
}

signed main() {
	init();
	n = rd(),q = rd(),s = rd();
	po = n;
	segIN::build(segIN::rt,1,n);
	segOUT::build(segOUT::rt,1,n);
	while(q--) {
		int opt = rd();
		switch(opt) {
			case 1:
				SigToSig();
				break;
			case 2:
				SigToRan();
				break;
			case 3:
				RanToSig();
				break;
			default:
				puts("Error!");
				exit(0);
				break;
		}
	}
	dij(s);
	for(int i = 1;i<=n;i++) 
		wt((dis[i] == inf) ? -1 : dis[i]),putchar(' ');

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值