P5304 [GXOI/GZOI2019]旅行者 题解
这题有 \(O(Tn\log n)\) 做法。
考虑类似求次短路的方式,对于每个点 \(i\) 求出能到它最近的关键点 \(x_i\),它能到最近的关键点 \(y_i\),即 \(\forall v,dis_{v\to i}\ge dis_{x_i\to i},dis_{i\to v}\ge dis_{i\to y_i}\),这一步正反图多源最短路跑两遍即可。
然后枚举每一条边 \((u,v,w)\),如果 \(x_u\ne y_v\cap x_u\ne0\cap y_v\ne0\),那么说明 \(x_u\to y_v\) 经过这条边并且这条路径一定是最短路,更新答案即可。
可以证明,答案一定会被包含并且非法状态不会被包含。
Code:
/*
========= Plozia =========
Author:Plozia
Problem:P5304 [GXOI/GZOI2019]旅行者
Date:2022/10/2
========= Plozia =========
*/
#include <bits/stdc++.h>
typedef long long LL;
using std::priority_queue;
const int MAXN = 1e5 + 5, MAXM = 5e5 + 5;
int n, m, k, Head[MAXN], cntEdge, Point[MAXN], Color[2][MAXN];
LL dis[2][MAXN];
bool book[MAXN];
struct node { int To; LL val; int Next; } Edge[MAXM];
struct EDGE { int x, y, z; } e[MAXM];
struct pri { int x; LL dis; bool operator <(const pri &fir)const { return (dis > fir.dis); } } ;
int Read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
LL Min(LL fir, LL sec) { return (fir < sec) ? fir : sec; }
void add(int x, int y, int z) { ++cntEdge; Edge[cntEdge] = (node){y, z, Head[x]}; Head[x] = cntEdge; }
void Dijkstra(int opt)
{
priority_queue <pri> q;
for (int i = 1; i <= n; ++i) book[i] = 0, dis[opt][i] = 0x7f7f7f7f7f7f7f7f, Color[opt][i] = 0;
for (int i = 1; i <= k; ++i) dis[opt][Point[i]] = 0, Color[opt][Point[i]] = Point[i], q.push((pri){Point[i], 0});
while (!q.empty())
{
int x = q.top().x; q.pop(); if (book[x]) continue ; book[x] = 1;
for (int i = Head[x]; i; i = Edge[i].Next)
{
int u = Edge[i].To; if (book[u]) continue ;
if (dis[opt][u] > dis[opt][x] + Edge[i].val)
{
dis[opt][u] = dis[opt][x] + Edge[i].val;
Color[opt][u] = Color[opt][x]; q.push((pri){u, dis[opt][u]});
}
}
}
}
void Solve()
{
n = Read(), m = Read(), k = Read();
for (int i = 1; i <= m; ++i) { e[i].x = Read(), e[i].y = Read(), e[i].z = Read(); add(e[i].x, e[i].y, e[i].z); }
for (int i = 1; i <= k; ++i) Point[i] = Read();
Dijkstra(0); cntEdge = 0; for (int i = 1; i <= n; ++i) Head[i] = 0;
for (int i = 1; i <= m; ++i) add(e[i].y, e[i].x, e[i].z);
Dijkstra(1); LL ans = 0x7f7f7f7f7f7f7f7f;
for (int i = 1; i <= m; ++i)
{
if (Color[0][e[i].x] != 0 && Color[1][e[i].y] != 0 && Color[0][e[i].x] != Color[1][e[i].y]) ans = Min(ans, dis[0][e[i].x] + dis[1][e[i].y] + e[i].z);
}
printf("%lld\n", ans); cntEdge = 0; for (int i = 1; i <= n; ++i) Head[i] = 0;
}
int main()
{
int t = Read(); while (t--) Solve(); return 0;
}