codeforces 787D Legacy (線段樹)

題目鏈接:http://codeforces.com/problemset/problem/787/D

Description

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.

Input

The first line of input contains three integers nq and s (1 ≤ n, q ≤ 105, 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 vu and w where wis the cost of that plan (1 ≤ v, u ≤ n, 1 ≤ w ≤ 109). Otherwise it is followed by four integers vlr and w where w is the cost of that plan (1 ≤ v ≤ n, 1 ≤ l ≤ r ≤ n, 1 ≤ w ≤ 109).

Output

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.

Examples

Input

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

Output

0 28 12 

Input

4 3 1
3 4 1 3 12
2 2 3 4 10
1 2 4 16

Output

0 -1 -1 12 

題目分析

看完題目,覺得是一個最短路問題,但是想到一種情況:如果每次操作讓一個點連接最大的區間,那麼建圖的時間將用時 O(n*q), 這樣是不行的,但是我們可以用線段樹來優化建圖過程。

線段樹的特點在於區間操作,比如求區間最值以及對區間進行整體操作等等,爲此我們將所有的頂點構成區間,建立線段樹: [1.n] , [1, (n+1)/2 ] , [ (n + 1 )/2 + 1, n ] .....由於我們將某一段連續的點當作線段樹上的一個頂點,這樣我們在建立 點到區間的邊 和 區間到點的邊的時候,就不用進行點和點之間的直接建邊了,而是在線段樹上進行頂點和頂點之間的建邊了,即直接建立區間和點之間的邊,這將節省建圖的時間。

而以區間 [1,n] 建立線段樹,自然會想到線段樹的葉子結點都是星球(點),那麼爲了體現從點x到區間建邊的操作,我們將線段樹所有的頂點當作求最短路時的頂點,也就是說,我們也會把一個區間看作一個頂點,求起點到各個頂點的最短距離,  圖如下所示

  

首先,因爲我們需要建立從點x到區間[a,b]的邊,那麼我們必須使得這個區間[a,b]代表的頂點到其子結點的距離爲0,這樣一來,我們在求最短路的時候,點x代表的頂點到區間[a,b]代表的頂點之間的距離是dis,同時點x到區間代表的點 a, a+1,a+2 ...b 的距離也會是dis,這樣也就實現了點x到區間[a,b]中的每一個點的建邊,由此我們需要建圖如下(圖中的藍線代表距離爲0的邊):

然後,我們需要建立從區間[a,b]到點x的邊,顯然,上面的圖中區間到點的距離都是0,明顯不符合我們的要求,我們需要構建一個新的圖。

我們必須使得點a,a+1...b代表的頂點到其父結點的距離爲0,這樣一來,我們在求最短路的時候,從區間[a,b]代表的頂點到點x代表的頂點之間的距離是dis,同時區間代表的點a, a+1,a+2 ...b 到點 x的距離也是dis,這樣也就實現了區間[a,b]中的每一個點到點x的建邊,由此我們需要建圖如下(圖中的藍線代表距離爲0的邊):

此時,我們構建出了兩個圖,第一個用於表示建立從點x到區間[a,b]的邊的時候的區間[a,b],第二個圖表示建立從區間[a,b]到點x的邊的時候的區間[a,b],而我們發現,建邊的時候的點x無論在哪個圖都會產生同樣的效果,因此兩個圖中的葉子結點是同一個點,爲了體現這一效果,我們可以將上述兩個圖合併爲如下的圖(圖中的藍線代表距離爲0的邊):

這樣一來,我們就可以正常地爲點到區間,區間到點建邊了,爲了方便,我們將3種建邊操作描述如下:

Type == 1 ,建立點u到點v之間的邊,權值爲w,由於起點和終點都是點,那麼由橙色的葉子結點[u,u]向綠色的葉子結點[v,v]建一條權值爲w的單向邊。

Type == 2,建立區間[a,b]到點u之間的邊,權值爲w,這個時候,我們由橙色的結點[a,b]向綠色的葉子結點[u.u]建一條權值爲w的單向邊。

Type == 3,建立點u到區間[a,b]之間的邊,權值爲w,這個時候,我們由橙色的葉子結點[u.u]向綠色的頂點[a,b]建一條權值爲w的單向邊。

嗯,方便起見,始終以下線段樹爲起點,以上線段樹爲終點,進行建邊

下面給出第二組樣例構建的圖,幫助理解

最後,我們從起點1對整個圖跑一遍dijkstra,得到各個葉子結點到起點1的距離:0,-1,-1,12

代碼區

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#define bug cout << "**********" << endl
//#define LOCAL = 1;
using namespace std;
typedef long long ll;
const ll inf = 1e18 + 5;
const int mod = 1e8;
const int Max = 1e5 + 10;

struct Node
{
	int l, r;
	int num;
}node1[Max << 3], node2[Max << 3];		//記錄上下兩個線段樹

struct Edge
{
	int to;
	ll dis;
};

vector<Edge>edge[Max << 3];				//記錄邊的信息,這裏用vector存圖導致了運行時間較長,請見諒
int pos1[Max << 3], pos2[Max << 3];		//記錄上下兩個樹中的每個葉子結點在線段樹中的下標
int n, q, s;
int tot;


void build_up(int l, int r, int num)	//自底向上建邊,下線段樹
{
	node1[num].l = l;
	node1[num].r = r;
	node1[num].num = ++tot;				//每一個頂點都有唯一的編號,便於建邊
	if (l == r)
	{
		pos1[l] = node1[num].num;		//記錄下線段樹的葉子結點對應的編號,藉助pos1直接找到葉子結點
		return;
	}
	int mid = (l + r) >> 1;
	build_up(l, mid, num << 1);
	build_up(mid + 1, r, num << 1 | 1);

	edge[node1[num << 1].num].push_back({ node1[num].num, 0 });
	edge[node1[num << 1 | 1].num].push_back({ node1[num].num, 0 });	//子結點向父結點建邊
}

void build_down(int l, int r, int num)	//自頂向下建邊
{
	node2[num].l = l;
	node2[num].r = r;
	node2[num].num = ++tot;
	if (l == r)
	{
		pos2[l] = node2[num].num;							//記錄上線段樹的葉子結點對應的編號,藉助pos1直接找到葉子結點
		edge[node2[num].num].push_back({ pos1[l], 0 });		//由上線段樹的葉子結點向下線段樹的葉子結點建邊
		return;
	}
	int mid = (l + r) >> 1;
	build_down(l, mid, num << 1);
	build_down(mid + 1, r, num << 1 | 1);
	edge[node2[num].num].push_back({ node2[num << 1].num, 0 });
	edge[node2[num].num].push_back({ node2[num << 1 | 1].num, 0 });	//父結點向子結點建邊
}

//由區間向點建邊
void upData_section_to_dot(int l, int r, int v, ll dis, int num)
{
	if (l <= node1[num].l && node1[num].r <= r)
	{
		edge[node1[num].num].push_back({ pos2[v], dis });	//由下線段樹的區間向上線段樹的葉子結點建邊
		return;
	}
	int mid = (node1[num].l + node1[num].r) >> 1;
	if (l <= mid)
		upData_section_to_dot(l, r, v, dis, num << 1);
	if (r > mid)
		upData_section_to_dot(l, r, v, dis, num << 1 | 1);
}

//由點向邊建邊
void upData_dot_to_section(int l, int r, int u, ll dis, int num)
{
	if (l <= node2[num].l && node2[num].r <= r)
	{
		edge[pos1[u]].push_back({ node2[num].num, dis });	//由下線段樹的點向上線段樹的區間建邊
		return;
	}
	int mid = (node2[num].l + node2[num].r) >> 1;
	if (l <= mid)
		upData_dot_to_section(l, r, u, dis, num << 1);
	if (r > mid)
		upData_dot_to_section(l, r, u, dis, num << 1 | 1);
}


bool vis[Max << 3];	//訪問狀態
ll dist[Max << 3];	//各個點到起點的距離

void dijkstra(int start)
{
	memset(vis, 0, sizeof(vis));	//初始化訪問狀態
	for (int i = 1;i <= 8 * n;i++)	//初始化各個點到起點的距離
		dist[i] = inf;
	dist[start] = 0;
	priority_queue < pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int> > >queues;//以到起點距離單增的優先隊列
	queues.push({ 0,start });		//起點入隊

	while (!queues.empty())
	{
		int u = queues.top().second;
		queues.pop();
		if (vis[u]) continue;		//重複訪問
		vis[u] = true;
		for (int i = 0;i < edge[u].size(); i++)
		{
			int v = edge[u][i].to;
			ll dis = edge[u][i].dis;
			if (vis[v]) continue;	//重複訪問
			if (dist[v] > dist[u] + dis)	//縮邊
			{
				dist[v] = dist[u] + dis;
				queues.push({ dist[v],v });
			}
		}
	}
	for (int i = 1;i <= n;i++)		//輸出結果
	{
		if (dist[pos1[i]] != inf)
			printf("%lld ", dist[pos1[i]]);
		else
			printf("-1 ");
	}
	printf("\n");
}

int main()
{
#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);	
	//這個是文件進行輸入和輸出,如果將13行的#define LOCAL = 1;註釋掉,就是常規運行了(黑框框裏面的那種)
#endif
	while (scanf("%d%d%d", &n, &q, &s) != EOF)
	{
		for (int i = 0;i < Max << 3; i++)	//多組輸入的初始化,其實這裏不需要多組輸入,習慣使然
			edge[i].clear();
		tot = 0;							//控制編號的變量
		build_up(1, n, 1);
		build_down(1, n, 1);				//構建上下線段樹
		while (q--)
		{
			int type, u, v, l, r;
			ll cost;
			scanf("%d", &type);
			if (type == 1)
			{
				scanf("%d%d%lld", &u, &v, &cost);
				edge[pos1[u]].push_back({ pos2[v], cost });	//由下線段樹的葉子結點向上線段樹的葉子結點建邊
			}
			else if (type == 2)
			{
				scanf("%d%d%d%lld", &u, &l, &r, &cost);
				upData_dot_to_section(l, r, u, cost, 1);	//由下線段樹的葉子結點向上線段樹的區間結點建邊
			}
			else
			{
				scanf("%d%d%d%lld", &v, &l, &r, &cost);
				upData_section_to_dot(l, r, v, cost, 1);	//由下線段樹的區間結點向上線段樹的葉子結點建邊
			}
		}
		dijkstra(pos1[s]);	//最短路
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章