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;
}

浙公网安备 33010602011771号