1. 程式人生 > 其它 >LuoguP3946 ことりのおやつ(小鳥的點心) 題解

LuoguP3946 ことりのおやつ(小鳥的點心) 題解

LuoguP3946 ことりのおやつ(小鳥的點心) 題解

Content

現給定一個有 \(n\) 個點和 \(m\) 條邊的雙向地圖,起點 \(s\)、目標點 \(t\),兩個係數 \(g,q\),還有每個點的兩個係數 \(h_i,l_i\),解釋如下。

這座城市經常下雪,今天亦是如此。每秒鐘會積上厚度為 \(q\) 的雪。第 \(i\) 個點在之前就已經積了厚度為 \(h_i\) 的雪,並且當積雪厚度大於 \(l_i\) 時無法行走。甜品店在 \(s\) 點,小 K 的家在 \(t\) 點。現在,小 K 想要知道在行走長度不超過 \(g\) 的情況下,從甜品店到她家的最短路徑長度。如果小 K 沒法回去,輸出 "wtnapは小鳥のおやつです!"(日語手動狗頭)

wtnap wa kotori no oyatsu desu!

資料範圍:\(2\leqslant n\leqslant 100000\)\(1\leqslant m\leqslant 500000\)\(1\leqslant s\)\(t\leqslant n\)\(0\leqslant g,q\leqslant10^9\)\(0\leqslant\text{每條邊的長度}\leqslant l_i\leqslant10^9\)

Solution

比較好想的一道 \(\texttt{dijkstra}\) 裸題。

為什麼呢?

首先我們來看一下題目,發現這和純 \(\texttt{dijkstra}\) 的唯一的區別是特判——特判積雪厚度。

那特判積雪厚度呢?

這很好辦,設從\(s\)點到\(i\)點的最短路徑長度為 \(dis_i\),則不能通過 \(i\) 點的特判為:\(dis_i\times q+h_i>l_i\)

所以,這道題目思路一下子就出來了——

  1. 跑一遍 \(\texttt{dijkstra}\),記錄下從 \(s\) 到各個點的最短距離(用 \(\texttt{dijkstra}\) 非常好辦)。

  2. 從點 \(1\)\(n\)(不包含 \(s\)\(t\))判斷上面的特判是否成立,成立的話封掉這個點。

  3. 做完操作 \(2\) 後再跑一遍 \(\texttt{dijkstra}\),求出最終的最短路徑。判斷是否超過 \(g\)

思路打通了之後,這道題目就不再難了。接下來就是將上面的操作用程式碼實現的問題了,在此不做贅述。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;

int n, m, s, t, g, q, h[1000007], cnt, dis[100007], vis[100007], del[100007];
struct edge {
	int v, to, nxt;
}e[1000007];
struct node {
	int hi, li;
}a[100007];

inline void a_e(int u, int v, int w) {
	e[++cnt] = (edge){w, v, h[u]}; h[u] = cnt;
	e[++cnt] = (edge){w, u, h[v]}; h[v] = cnt;
}

inline void dj(int s, int t) {
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	priority_queue<pair<int, int> > q;
	q.push(make_pair(0, s));
	dis[s] = 0;
	while(!q.empty()) {
		int x = q.top().second; q.pop();
		if(vis[x])	continue;
		vis[x] = 1;
		for(int i = h[x]; i; i = e[i].nxt) {
			int y = e[i].to, z = e[i].v;
			if(del[y])	continue;
			if(dis[y] > dis[x] + z) {
				dis[y] = dis[x] + z;
				q.push(make_pair(-dis[y], y));
			}
		}
	}
}

int main() {
	scanf("%d%d%d%d%d%d", &n, &m, &s, &t, &g, &q);
	for(int i = 1; i <= n; ++i)	scanf("%d%d", &a[i].hi, &a[i].li);
	for(int i = 1; i <= m; ++i) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a_e(x, y, z);
	}
	dj(s, t);
	for(int i = 1; i <= n; ++i) {
		if(i == s || i == t)	continue;
		if(dis[i] * q + a[i].hi > a[i].li)	del[i] = 1;
	}
	dj(s, t);
//	printf("%d\n", dis[t]);
	if(dis[t] > g)	printf("wtnap wa kotori no oyatsu desu!");
	else	printf("%d", dis[t]);
	return 0;
}