Description
给你 n n n 个点 m m m 条边的有向图,求给定 k k k 个特殊点两两之间最短路的最小值。 T T T 组询问。
1 ≤ T ≤ 5 , 1 ≤ k ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 5 × 1 0 5 , 1 ≤ z ≤ 2 × 1 0 9 1 \leq T \leq 5,1 \leq k \leq n \leq 10^5,1 \leq m \leq 5 \times 10^5,1 \leq z \leq 2 \times 10^9 1≤T≤5,1≤k≤n≤105,1≤m≤5×105,1≤z≤2×109
Solution
两种思路。
第一种:
对于每一条边 ( x , y , z ) (x,y,z) (x,y,z) ,离 x x x 最近的特殊点是 a a a,离 y y y 最近的特殊点是 b b b。那么,这两个特殊点的距离是 d i s a + d i s b + z dis_a + dis_b + z disa+disb+z。我们要找到最小值。
因为这是有向图,所以正反跑两次 Dijkstra 就可以了进行染色就可以了,染色是记录离每个点最近的特殊点。如果 a = b a = b a=b,那么就相当于走了一个环,所以我们要特判。
复杂度 O ( T × n l o g n ) O(T \times nlogn) O(T×nlogn)。
第二种:
将所有的点分成两个集合 A A A 和 B B B。我们用源点 s s s 连接集合 A A A 中所有的点,边权都为 0 0 0,再将集合 B B B 连向汇点 t t t,边权都为 0 0 0。集合内部边权不变。然后跑 A A A 到 B B B 的最短路。
将所有节点分成两个集合,要满足任何两个特殊点都有至少一次被分进了不同的集合。可以按照每个点的编号的二进制的第 i i i 位是 0 0 0 还是 1 1 1 枚举。只要是两个不同的点,一定会被分进两个不同的集合。同样,因为也是有向图,也要跑两边 Dijkstra。
复杂度 O ( T × n l o g 2 n ) O(T \times nlog^2n) O(T×nlog2n)。
Code
#include <bits/stdc++.h>
using namespace std;
#define re register
#define F first
#define S second
typedef long long ll;
typedef pair<int, int> P;
const int N = 1e6 + 6;
const int INF = 0x3f3f3f3f;
inline int read() {
int X = 0,w = 0; char ch = 0;
while(!isdigit(ch)) {w |= ch == '-';ch = getchar();}
while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
return w ? -X : X;
}
int Start[N], To[N], Dis[N], color[2][N], a[N], head[N], tot;
ll dis[2][N];
int n, m, k;
struct edge{
int to, w, nxt;
}e[N];
inline void addedge(int x, int y, int z){
e[++tot].to = y; e[tot].w = z; e[tot].nxt = head[x]; head[x] = tot;
}
struct node{
int x; ll w;
};
bool operator < (node x, node y){
return x.w > y.w;
}
void Dijkstra(int p){
memset(color[p], 0, sizeof(color[p])); memset(dis[p], 0x7f, sizeof(dis[p]));
priority_queue <node> q;
for (re int i = 1; i <= k; i++) {
dis[p][a[i]] = 0; color[p][a[i]] = a[i];
q.push((node){a[i], 0});
}
while (!q.empty()){
int x = q.top().x; ll w = q.top().w; q.pop();
if (w != dis[p][x]) continue;
for (re int i = head[x]; i; i = e[i].nxt){
int y = e[i].to, ww = e[i].w;
if (dis[p][y] > dis[p][x] + ww){
dis[p][y] = dis[p][x] + ww;
color[p][y] = color[p][x];
q.push((node){y, dis[p][y]});
}
}
}
}
int main(){
int T = read();
while (T--){
n = read(), m = read(), k = read();
memset(head, 0, sizeof(head)); tot = 0;
for (re int i = 1; i <= m; i++){
int x = read(), y = read(), z = read();
Start[i] = x; To[i] = y; Dis[i] = z;
if (x != y) addedge(x, y, z);
}
for (re int i = 1; i <= k; i++) a[i] = read();
Dijkstra(0);
memset(head, 0, sizeof(head)); tot = 0;
for (int i = 1; i <= m; i++)
if (Start[i] != To[i]) addedge(To[i], Start[i], Dis[i]);
Dijkstra(1);
ll ans = 1e18;
for (re int i = 1; i <= m; i++){
int x = Start[i], y = To[i], z = Dis[i];
if (color[0][x] && color[1][y] && color[0][x] != color[1][y])
ans = min(ans, z + dis[0][x] + dis[1][y]);
}
printf("%lld\n", ans);
}
return 0;
}