题目
思路
思路一:暴力
题解区就有这样的一篇题解:暴力艹标程。
然后就加了一组数据把它卡掉了……
思路二:多源点
把所有的点一口气塞进去。
先在正向图上跑一边;再在反向图上跑一边。
然后枚举一条边作为必经之路。
战斗结束了。
当然有一个要点:存储一下是从谁到达了自己。也就是说,它究竟是距离哪个特殊点最近。这条边两边的 f r o m from from不同才可更新。显然,最短的点对之间的路径上一定存在这样的一条边。
思路三:奇技淫巧
谁脑洞这么大,想到这种方法
延续上面的多源点方法。
最暴力的方法,之所以会被卡掉,完全是因为它搜索了太多不可能更优的情况。
那么,多源点就可以很好的解决问题——这些点一起出发,一旦搜到一个终点就停止。
问题就是怎么选择这些多源点?毕竟,如果你不使用双向最短路,两个源点之间的路径就不会被考虑!
答案是,枚举二进制中的某一位,按照这一位上的取值分类。也就是说,如果 x x x的第 i i i位是 1 1 1,就作为源点。
为什么它是对的呢?因为 x , y x,y x,y至少有一位是不同的,那么就会被考虑到。
简直是奥妙重重!
代码
我写的是思路二
版本的。
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(long long x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar(x%10+'0');
}
const int MaxN = 200005;
const long long infty = (1ll<<60)-1;
int n, m, k;
namespace Graph{
struct Edge{
int to, nxt, val;
Edge(int T=0,int N=0,int V=0){
to = T, nxt = N, val = V;
}
} edge[MaxN*10];
int cntEdge, head[MaxN<<1];
void clear(int N){
for(int i=1; i<=N; ++i)
head[i] = -1;
cntEdge = 0;
}
void addEdge(int from,int to,int val){
edge[cntEdge] = Edge(to,head[from],val);
head[from] = cntEdge ++;
}
} using namespace Graph;
void input(){
n = readint(), m = readint(), k = readint();
clear(n<<1);
for(int i=1,u,v,w; i<=m; ++i){
u = readint(), v = readint(), w = readint();
addEdge(u,v,w); addEdge(v+n,u+n,w);
} /* 编号(n,2n]是反图 */
}
long long dis[MaxN<<1]; int from[MaxN<<1];
struct Status{
int x; long long val;
bool operator<(const Status &that)const{
return val > that.val;
}
Status(int X=0,long long V=0):x(X),val(V){ }
};
priority_queue<Status> pq;
void dijkstra(){
Status t;
while(not pq.empty()){
t = pq.top(), pq.pop();
if(dis[t.x] < t.val) continue;
for(int i=head[t.x]; ~i; i=edge[i].nxt)
if(dis[edge[i].to] > t.val+edge[i].val){
dis[edge[i].to] = t.val+edge[i].val;
from[edge[i].to] = from[t.x];
pq.push(Status(edge[i].to,dis[edge[i].to]));
}
}
}
void solve(){
for(int i=1; i<=(n<<1); ++i)
dis[i] = infty, from[i] = (i-1)%n+1;
for(int i; k; --k){
i = readint();
dis[i] = dis[i+n] = 0;
pq.push(Status(i,0));
pq.push(Status(i+n,0));
}
dijkstra();
long long ans = infty;
for(int x=1; x<=n; ++x)
for(int i=head[x]; ~i; i=edge[i].nxt)
if(from[edge[i].to+n] != from[x])
ans = min(ans,dis[x]+dis[edge[i].to+n]+edge[i].val);
writeint(ans), putchar('\n');
}
int main(){
for(int T=readint(); T; --T)
input(), solve();
return 0;
}