Description
给定一个 \(n\)个 点、 \(m\) 条边的带权无向图,其中有 \(s\) 个点是加油站。
每辆车都有一个油量上限 \(b\) ,即每次行走距离不能超过 \(b\) ,但在加油站可以补满。
\(q\) 次询问,每次给出 \(x,y,b\) ,表示出发点是 \(x\) ,终点是 \(y\) ,油量上限为 \(b\) ,且保证 \(x\) 点和 \(y\) 点都是加油站,请回答能否从 \(x\) 走到 \(y\) 。
Input
第一行包含三个正整数 \(n,s,m(2\le s\le n\le 200000,1\le m\le 200000)\) ,表示点数、加油站数和边数。
第二行包含 \(s\) 个互不相同的正整数 $c[1],c[2],\cdots cs $ ,表示每个加油站。
接下来 \(m\) 行,每行三个正整数 \(u[i],v[i],d[i](1\le u[i],v[i]\le n,u[i]\ne v[i],1\le d[i]\le 10000)\) ,表示 \(u[i]\) 和 \(v[i]\) 之间有一条长度为 \(d[i]\) 的双向边。
接下来一行包含一个正整数 \(q(1\le q\le 200000)\) ,表示询问数。
接下来 \(q\) 行,每行包含三个正整数 \(x[i],y[i],b[i](1\le x[i],y[i]\le n,x[i]\ne y[i],1<\le b[i]\le 2\times 10^9)\) ,表示一个询问。
Output
输出 \(q\) 行。第 \(i\) 行输出第i个询问的答案,如果可行,则输出 \(\mathrm{TAK}\) ,否则输出 \(\mathrm{NIE}\) 。
Sample
Sample Input
6 4 5
1 5 2 6
1 3 1
2 3 2
3 4 3
4 5 5
6 4 5
4
1 2 4
2 6 9
1 5 9
6 5 8
Sample Output
TAK
TAK
TAK
NIE
Solution
真是一道结论诡好题。
大家肯定知道,不是加油站的点是废点。那加油站点该怎么重新建图呢?
来看一个图。红点表示加油站,黑点是废点。
从 \(1\) 到 \(3\) 走简单路径会至少需要 \(7\) 的油量,而从 \(1\) 到 \(4\) 再到 \(3\) 则只需要准备 \(5\) 的油量就可以了。这是因为 \[c<a 且 c<b \] 所以 \[c+b<a+b 且c+a<b+a\] 于是我们就得到了结论
- 从当前节点到最近的加油站再到其它的加油站不会更差
那么就可以多源最短路,最小生成树判断连通性就可以了。
具体细节见代码。
#include<bits/stdc++.h>
using namespace std;
#define N 400011
#define rep(i, a, b) for (int i = a; i <= b; i++)
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
int n, s, m, C[N], head[N], tot = 1, cnt, dis[N], near[N], fa[N];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
queue<int> q;
bool inq[N], ans[N];
struct edge { int v, w, next; }e[N];
inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
struct edgeData {
int u, v, w;
edgeData(int _u = 0, int _v = 0, int _w = 0):u(_u), v(_v), w(_w) {}
bool operator < (const edgeData& b) const { return w < b.w; }
}edt[N];
struct query {
int id, S, T, d;
bool operator < (const query& b) const { return d < b.d; }
}qu[N];
void spfa() {
rep(i, 1, n) dis[i] = 0x7fffffff;
rep(i, 1, s) q.push(C[i]), inq[C[i]] = 1, dis[C[i]] = 0, near[C[i]] = C[i];
while (!q.empty()) {
int u = q.front(); q.pop(), inq[u] = 0;
for (int i = head[u], v; i; i = e[i].next) if (dis[v = e[i].v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w, near[v] = near[u];
if (!inq[v]) q.push(v), inq[v] = 1;
}
}
rep(u, 1, n) for (int i = head[u], v; i; i = e[i].next) if (near[u] ^ near[v = e[i].v])
edt[++cnt] = edgeData(near[u], near[v], dis[u] + dis[v] + e[i].w);
sort(edt + 1, edt + 1 + cnt);
}
int main() {
n = read(), s = read(), m = read();
rep(i, 1, n) fa[i] = i;
rep(i, 1, s) C[i] = read();
rep(i, 1, m) { int u = read(), v = read(), w = read(); add(u, v, w), add(v, u, w); }
spfa();
int q = read();
rep(i, 1, q) qu[i].S = read(), qu[i].T = read(), qu[i].d = read(), qu[i].id = i;
sort(qu + 1, qu + 1 + q);
int pos = 1;
rep(i, 1, q) {
while (pos <= cnt && edt[pos].w <= qu[i].d) {
int x = find(edt[pos].u), y = find(edt[pos].v);
if (x ^ y) fa[x] = y;
pos++;
}
ans[qu[i].id] = (find(qu[i].S) == find(qu[i].T));
}
rep(i, 1, q) puts(ans[i] ? "TAK" : "NIE");
return 0;
}