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\)。
所以,這道題目思路一下子就出來了——
-
跑一遍 \(\texttt{dijkstra}\),記錄下從 \(s\) 到各個點的最短距離(用 \(\texttt{dijkstra}\) 非常好辦)。
-
從點 \(1\) 到 \(n\)(不包含 \(s\) 和 \(t\))判斷上面的特判是否成立,成立的話封掉這個點。
-
做完操作 \(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;
}