传送门
做法应该不难想到:对a求一遍最短路,对b求一遍最短路,然后看a->b和b->c的交了多少条边,贪心的把交边赋值成为小权值即可。
难点在于这个交集如何去求解,不妨换一种思路,去枚举这个交点x,那么路径的走向就是a->x->b->x->c.可以观察到b->x的路径走了两遍,所以只需要把b->x路径上的边赋最小的若干个权值即可。
具体做法就是先对p数组排序求一遍前缀和,然后a,b,c各bfs求一遍最短路。枚举x每次统计p[dis[x][1]] + p[dis[x][0]+dis[x][1]+dis[x][2]]的最小值即可。
要注意如果dis[x][0]+dis[x][1]+dis[x][2] > m了,说明以x为交点的路径是不正确的,即x无法作为交点。
代码:
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define pb push_back
using namespace std;
const int N = 4e5+10;
int nxt[N],n,head[N],to[N],ct,dis[N][3],vis[N];
long long p[N];
void addedge(int x,int y){
nxt[++ct] = head[x];head[x] = ct;to[ct] = y;
}
void bfs(int s,int op){
queue<int> q;
q.push(s);
fir(i,1,n) vis[i] = 0,dis[i][op] = 0;
vis[s] = 1;
while(q.size()){
int u = q.front();q.pop();
for(int i = head[u];i;i = nxt[i]){
int y = to[i];
if(vis[y]) continue;
vis[y] = 1;
q.push(y);
dis[y][op] = dis[u][op] + 1;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--){
int m,a,b,c;
cin >> n >> m >> a >> b >> c;
fir(i,1,n) head[i] = vis[i] = 0;
ct = 1;
fir(i,1,m) cin >> p[i];
sort(p+1,p+1+m);
sort(p+1,p+1+m);
fir(i,1,m*2) p[i] += p[i-1];
fir(i,1,m){
int x,y;
cin >> x >> y;
addedge(x,y);
addedge(y,x);
}
bfs(a,0);bfs(b,1);bfs(c,2);
long long ans = 1e17;
fir(i,1,n){
if(dis[i][0]+dis[i][1]+dis[i][2] > m) continue;
ans = min(ans,p[dis[i][1]] + p[dis[i][0]+dis[i][1]+dis[i][2]]);
}
cout << ans << "\n";
}
return 0;
}