题目大意:
给定n个点m条边的无向无权图,以及m个价格,要你把m个价格分配给m条路,使得从a点走到b点,再从b点走到c点的总花费最小。
解法:
从a走到b,再走到c,显然都是走步数最短的路径。两条路径要么不相交,要么只会在一个点相交。
所以我们可以枚举相交的点x,那么整个路径可分成:
(
a
=
>
x
)
+
(
x
=
>
b
)
+
(
b
=
>
x
)
+
(
x
=
>
c
)
(a => x) + (x => b) +(b => x) + (x => c)
(a=>x)+(x=>b)+(b=>x)+(x=>c) 四段
也就是
d
i
s
(
x
,
a
)
+
2
∗
d
i
s
(
x
,
b
)
+
d
i
s
(
x
,
c
)
dis(x,a) + 2 * dis(x,b) + dis(x,c)
dis(x,a)+2∗dis(x,b)+dis(x,c)
因为x~b需要走两次,所以优先把花费小的路分配给 x ~ b,再把次小一些的分配给 x ~a 和 x ~ c
可以通过前缀和来完成每次O(1) 计算贡献,最后取最小值即可。
Code:
const int INF = 0x3f3f3f3f;
const int MX = 2e5 + 7;
int n,m;
int a,b,c;
int v[MX];
ll dis[MX][5];
vector<int>Edge[MX];
void bfs(int st,int id){
bool vis[MX];
queue<int>q;
for(int i = 1;i <= n;++i){
dis[i][id] = INF;
vis[i] = false;
}
dis[st][id] = 0;
q.push(st);
vis[st] = true;
while(!q.empty()){
int u = q.front();q.pop();
for(auto v : Edge[u]){
if(vis[v]) continue;
dis[v][id] = dis[u][id] + 1;
vis[v] = true;
q.push(v);
}
}
}
ll solve(){
ll pre[MX];
ll res = LINF, tmp = 0;
bfs(a,0);bfs(b,1);bfs(c,2);
sort(v + 1,v + 1 + m);
for(int i = 1;i <= m;++i) pre[i] = pre[i - 1] + 1ll * v[i];
for(int i = 1;i <= n;++i){
int x = i;
if(1ll * dis[x][0] + dis[x][1] + dis[x][2] <= 1ll * m) {tmp = pre[dis[x][0] + dis[x][1] + dis[x][2]] + pre[dis[x][1]],res = min(res,tmp);}
else continue;
}
return res;
}
void init(){
for(int i = 1;i <= n;++i) {Edge[i].clear();dis[i][0] = dis[i][1] = dis[i][2] = 0;}
}
int main()
{
int T;scanf("%d",&T);
while(T--){
scanf("%d %d %d %d %d",&n,&m,&a,&b,&c);init();
for(int i = 1;i <= m;++i) scanf("%d",&v[i]);
for(int i = 1;i <= m;++i){
int u,v;scanf("%d %d",&u,&v);
Edge[u].pb(v);Edge[v].pb(u);
}
ll res = solve();printf("%lld\n", res);
}
return 0;
}