CF2057E Another Exercise on Graphs 题解

对于这种问题,我们有一个(很典?)的套路。
直接做貌似不可做,故要尝试去【判定】一条边是否是答案。

判断一条边 (u,v,w) 是否是第 k 大的,只需要把图上所有大于 w 的边权值重设为 1,小于等于 w 的边权值重设为 0,看看 uv 的最短路是不是等于 k1,是则就是答案。

感性理解:我们要找的是所有路径中,最小的第 k 大,重构图之后的最短路就是包含【当前判定的边】的路径(显然),这条路径如果长度为 k1,那么只有 k1 条边小于 w,那么等价于 w 是第 k 大。

那我们就很好搞了。
我们首先算出重构图中边全是 1 时的全源最短路,再从小到大枚举所有边,依次从【重构图】中把每条边改为 0,用 Floyd 松弛一遍,就得到了每一【时刻】的全源最短路。
判定的时候二分一下,即可每个询问 O(logm) 的时间回答。

E1 的话,在预处理最短路的时候直接 O(mn2) 做就行。E2,发现在松弛某条边的时候,如果两端点的 dis 已经等于 0,就 skip 掉。这样做相当于每次将两个点【合并为一个点】,所以只会松弛 n1 轮,总时间复杂度 O(n3+qlogm)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int> 
#define all(v) v.begin(), v.end()
using namespace std;

//#define filename "xxx" 
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
#define multi_cases 1

namespace Traveller {
	const int N = 402, M = 1.6e5+2;
	
	int n, m, q, g[N][N];
	struct edge {
		int u, v, w;
		edge() { }
		edge(int u, int v, int w) : u(u), v(v), w(w) { }
	} e[M];
	
	int f[N][N][N], a[M];
	
	void main() {
		cin >> n >> m >> q;
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j) g[i][j] = 0;
		for(int i = 1, u, v, w; i <= m; ++i) {
			scanf("%d%d%d", &u, &v, &w);
			g[u][v] = g[v][u] = w;
			e[i] = edge(u, v, w);
		}
		
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j)
				f[0][i][j] = g[i][j] ? 1 : i == j ? 0 : inf;
		
		for(int k = 1; k <= n; ++k)
			for(int i = 1; i <= n; ++i)
				for(int j = 1; j <= n; ++j)
					f[0][i][j] = min(f[0][i][j], f[0][i][k] + f[0][k][j]);
		
		sort(e+1, e+m+1, [] (edge x, edge y) { return x.w < y.w; });
		int tot = 0;
		for(int k = 1; k <= m; ++k) {
			int u = e[k].u, v = e[k].v, w = e[k].w;
			if(f[tot][u][v] == 0) continue;
			a[++tot] = w;
			for(int i = 1; i <= n; ++i)
				for(int j = 1; j <= n; ++j) f[tot][i][j] = f[tot-1][i][j];
			f[tot][u][v] = f[tot][v][u] = 0;
			for(int i = 1; i <= n; ++i)
				for(int j = 1; j <= n; ++j)
					f[tot][i][j] = min(f[tot][i][j], min(f[tot-1][i][u] + f[tot-1][v][j], f[tot-1][i][v] + f[tot-1][u][j]));
		}
		
		for(int i = 1, u, v, k; i <= q; ++i) {
			scanf("%d%d%d", &u, &v, &k);
			int L = 1, R = tot;
			while(L < R) {
				int mid = L + R >> 1;
				if(f[mid][u][v] < k) R = mid;
				else L = mid + 1;
			}
			printf("%d ", a[L]);
		}
		puts("");
	}
}

signed main() {
#ifdef filename
	FileOperations();
#endif
	
	signed _ = 1;
#ifdef multi_cases
	scanf("%d", &_);
#endif
	while(_--) Traveller::main();
	return 0;
}


posted @   wfc284  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示