CF2057E Another Exercise on Graphs 题解

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

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

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

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

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

#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 @ 2025-01-16 15:12  Water_M  阅读(29)  评论(0)    收藏  举报