Codeforces Round #636 E. Weights Distributing(最短路)
You are given an undirected unweighted graph consisting of nn vertices and mm edges (which represents the map of Bertown) and the array of prices pp of length mm . It is guaranteed that there is a path between each pair of vertices (districts).
Mike has planned a trip from the vertex (district) aa to the vertex (district) bb and then from the vertex (district) bb to the vertex (district) cc . He can visit the same district twice or more. But there is one issue: authorities of the city want to set a price for using the road so if someone goes along the road then he should pay the price corresponding to this road (he pays each time he goes along the road). The list of prices that will be used pp is ready and they just want to distribute it between all roads in the town in such a way that each price from the array corresponds to exactly one road.
You are a good friend of Mike (and suddenly a mayor of Bertown) and want to help him to make his trip as cheap as possible. So, your task is to distribute prices between roads in such a way that if Mike chooses the optimal path then the price of the trip is the minimum possible. Note that you cannot rearrange prices after the start of the trip.
You have to answer tt independent test cases.
The first line of the input contains one integer tt (1≤t≤1041≤t≤104 ) — the number of test cases. Then tt test cases follow.
The first line of the test case contains five integers n,m,a,bn,m,a,b and cc (2≤n≤2⋅1052≤n≤2⋅105 , n−1≤m≤min(n(n−1)2,2⋅105)n−1≤m≤min(n(n−1)2,2⋅105) , 1≤a,b,c≤n1≤a,b,c≤n ) — the number of vertices, the number of edges and districts in Mike's trip.
The second line of the test case contains mm integers p1,p2,…,pmp1,p2,…,pm (1≤pi≤1091≤pi≤109 ), where pipi is the ii -th price from the array.
The following mm lines of the test case denote edges: edge ii is represented by a pair of integers vivi , uiui (1≤vi,ui≤n1≤vi,ui≤n , ui≠viui≠vi ), which are the indices of vertices connected by the edge. There are no loops or multiple edges in the given graph, i. e. for each pair (vi,uivi,ui ) there are no other pairs (vi,uivi,ui ) or (ui,viui,vi ) in the array of edges, and for each pair (vi,ui)(vi,ui) the condition vi≠uivi≠ui is satisfied. It is guaranteed that the given graph is connected.
It is guaranteed that the sum of nn (as well as the sum of mm ) does not exceed 2⋅1052⋅105 (∑n≤2⋅105∑n≤2⋅105 , ∑m≤2⋅105∑m≤2⋅105 ).
For each test case, print the answer — the minimum possible price of Mike's trip if you distribute prices between edges optimally.
2 4 3 2 3 4 1 2 3 1 2 1 3 1 4 7 9 1 5 7 2 10 4 8 5 6 7 3 3 1 2 1 3 1 4 3 2 3 5 4 2 5 6 1 7 6 7
7 12
我一开始想的是求出a到b的最短路和b到c的最短路,统计哪些路被重复走了,就优先分配小的权重。但这样是不对的。直接复制CF题解评论里的一段话:“You cannot do the question on basis of only considering shortest from A to B and then shortest from B to C separately. Consider smallest path from A to B(length s)and there is another path from A to B(length s+1)which has only one more edge then the smallest one.Suppose that path contains C, so that is more advantageous and we need only smallest s+1 prices while with the shortest path we will need then s+k prices (k distance of B from C).”
正解是sort完边权后先求前缀和。从a,b,c这三个点出发,找到这三个点到其余点的最短路径长,然后枚举每个点x(可以是abc自己),可以发现路径由这么四部分构成:a->x x->b b->x x->c 其中x->b相当于走了两次,所以优先分给x->b路径小权值的边,然后是a->x和x->c。可以利用前缀和来更新累加答案。
可能会想到重复走了某些边怎么办,比如x-a-b-c这种链的情况。其实这没有影响,因为这种情况必然不可能是最优解。观察就会发现,最优解只可能是类似样例里的那样。
注意最后更新答案的时候要判定一下,路径长度超过m直接跳过。
#include <bits/stdc++.h> #define N 200005 #define M 200005 using namespace std; int n,m,a,b,c,head[N],ver[2*M],Next[2*M],dist[3][N],tot=0; long long edge[M]; void add(int x,int y) { ver[++tot]=y,Next[tot]=head[x],head[x]=tot; } struct node { int num; int cnt; }; void bfs(int num,int x) { bool vis[200005]={0}; queue<node>q; node start={num,0}; q.push(start); while(q.size()) { node pre=q.front(); q.pop(); if(vis[pre.num])continue; vis[pre.num]=1; dist[x][pre.num]=pre.cnt; int i; for(i=head[pre.num];i;i=Next[i]) { int y=ver[i]; if(vis[y]) continue; node nxt; nxt.cnt=pre.cnt+1; nxt.num=y; q.push(nxt); } } } int main() { int t; cin>>t; while(t--) { tot=0; scanf("%d%d%d%d%d",&n,&m,&a,&b,&c); long long i; for(i=1;i<=max((long long)n,min((long long)200002,(long long)n*(n-1)/2));i++) { dist[0][i]=dist[1][i]=dist[2][i]=head[i]=ver[i]=Next[i]=edge[i]=0; } for(i=1;i<=m;i++)scanf("%lld",&edge[i]); sort(edge+1,edge+m+1); edge[0]=0; for(i=1;i<=m;i++)edge[i]+=edge[i-1];//求前缀和方便后续 for(i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } bfs(a,0); bfs(b,1); bfs(c,2); long long ans=~0ull>>2; for(i=1;i<=n;i++)//枚举点 { if(dist[0][i]+dist[1][i]+dist[2][i]>m)continue; long long temp=edge[dist[0][i]+dist[1][i]+dist[2][i]]+edge[dist[1][i]]; ans=min(ans,temp); } cout<<ans<<endl; } return 0; }