E. Weights Distributing(思维,单源最短路交集)

传送门
做法应该不难想到:对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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45590210/article/details/111711800