给定一个n个点、m条边的带权无向图,其中有s个点是加油站。
每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满。
q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走到y。
如果我们将数据范围改变到 O ( n 2 ) O(n^2) O(n2)级别的话,我们可以将每一个加油站之间按照最短路径为边权,两两连边。在询问时我们只需要找到两点间最大边权值,与给定的数值进行比较即可。
要求任意两点间最大边权最小,所求即为瓶颈生成树,就是最小生成树。
但是我们显然无法做到对于任意两个加油站之间都有连边,因此我们需要找到某一种方法来优化建图。考虑 x x x带 y y y最短路径中的某一个点 p p p.
距离点 p p p最近的加油站不是 x x x和 y y y,设该点为 c c c,则 max ( x , c ) , max ( y , v ) ≤ max ( x , y ) . \max(x,c),\max(y,v)\le \max(x,y). max(x,c),max(y,v)≤max(x,y).那么 x x x到 y y y的边完全可以通过 ( x , c ) (x,c) (x,c)以及 ( c , y ) (c,y) (c,y)
来经过.
若离 p p p最近的节点是 x x x或是 y y y,那么 ( x , p ) (x,p) (x,p)和 ( p , y ) (p,y) (p,y)依然没有影响。
因此,我们若向每一个节点相邻的最近的点去建边的话,就可以代替任意两点之间的连边了。对于边 ( x , y , v ) (x,y,v) (x,y,v),我们建边为 ( min x , min y , d i s x + d i s y + v ) (\min_x,\min_y,dis_x+dis_y+v) (minx,miny,disx+disy+v). min x \min_x minx表示节点 x x x所能到达的最近的加油站, d i s x dis_x disx表示节点 x x x到 min x \min_x minx的距离。
用上述边权建立最小生成树即可。
最小生成树求完以后不需要使用LCA,直接用离线处理即可。
#include
#define int long long
using namespace std;
const int N = 8e5;
int n, m, s, k, Q;
pair< int, pair<int,int> > e[N * 2];
int dis[N], path[N], key[N], vis[N], fa[N], ans[N];
vector < pair<int,int> > G[N];
struct node { int x, y, v, id; } q[N];
int read(void)
{
int s = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') s = s * 10 + c - 48, c = getchar();
return s;
}
void Dijkstra(void)
{
priority_queue < pair<int,int> > q;
memset(vis, 0, sizeof vis);
memset(dis, 30, sizeof dis);
for (int i=1;i<=s;++i)
{
dis[key[i]] = 0;
path[key[i]] = key[i];
q.push({0, key[i]});
}
while (q.size())
{
int x = q.top().second; q.pop();
if (vis[x]) continue; vis[x] = 1;
for (int i=0;i<G[x].size();++i)
{
int y = G[x][i].first;
int v = G[x][i].second;
if (dis[x] + v < dis[y]) {
dis[y] = dis[x] + v;
path[y] = path[x];
q.push({-dis[y], y});
}
}
}
return;
}
int get(int x) {
if (fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
bool Pig(node p1, node p2) {
return p1.v < p2.v;
}
void Kruscal(void)
{
for (int i=1;i<=n;++i) fa[i] = i;
sort(e + 1, e + k + 1);
sort(q + 1, q + Q + 1, Pig);
int j = 1;
for (int i=1;i<=Q;++i)
{
while (e[j].first <= q[i].v and j <= k)
{
int x = e[j].second.first;
int y = e[j].second.second;
fa[get(x)] = get(y), j ++;
}
ans[q[i].id] = get(q[i].x) == get(q[i].y);
}
return;
}
signed main(void)
{
n = read(), s = read(), m = read();
for (int i=1;i<=s;++i) key[i] = read();
for (int i=1;i<=m;++i)
{
int x = read(), y = read(), v = read();
G[x].push_back({y, v});
G[y].push_back({x, v});
}
Dijkstra();
for (int x=1;x<=n;++x) {
for (int i=0;i<G[x].size();++i)
{
int y = G[x][i].first;
int v = G[x][i].second + dis[x] + dis[y];
if (y > x) continue;
if (path[x] == path[y]) continue;
e[++ k] = {v, {path[x], path[y]}};
}
}
Q = read();
for (int i=1;i<=Q;++i) {
int x = read(), y = read(), v = read();
q[i] = {x, y, v, i};
}
Kruscal();
for (int i=1;i<=Q;++i) puts(ans[i] ? "TAK" : "NIE");
return 0;
}