前置知识:floyd
题意
给定一个 \(n\) 个点 \(m\) 条边的无向简单图,对于每对 \((s,t), 1 \le s < t \le n\),求出有多少条边被至少一个 \(s \to t\) 的路径经过。
\(n \le 500, m \le \frac{n(n - 1)}{2}\)
题解
首先一个很一眼的做法先 floyd 一遍求出 \(dis(i, j)\),接着枚举 \((s, t)\),然后再枚举边 \((u, v, w)\),如果有:
\[dis(s, u) + w + dis(v, t) = dis(s, t) \lor \\ dis(s, v) + w + dis(u, t) = dis(s, t) \]那么 \((u, v, w)\) 会被 \(s \to t\) 的至少一条最短路经过,统计这种边的数量即可。时间复杂度 \(O(n^2m)\),考虑优化成 \(O(n^3)\)。
上面的做法的复杂度劣是因为枚举了边,考虑枚举一个中转点 \(p\),满足:
\[dis(s, p) + dis(p, t) = dis(s, t) \]再考虑 \(p\) 对答案的贡献。考虑算 \(s \to p\) 的最短路的最后一条边的种类数 \(cnt_p\),则 \(p\) 对答案的贡献就是 \(cnt_p\)。对 \(cnt_p\) 的预处理方法是枚举边 \((q, p, l)\),且满足 \(dis(s, q) + l = dis(p)\),\(cnt_p\) 等于这种边的数量。
此题主要考查了对 floyd 的运用,比较关键的思维转化是枚举中转点并计算其贡献的步骤。
code:
#include <bits/stdc++.h>
#define vi vector <int>
#define vl vector <i64>
#define pii pair <int, int>
#define pll pair <i64, i64>
#define mp make_pair
#define tpi tuple <int, int, int>
#define tpl tuple <i64, i64, i64>
#define mt make_tuple
#define eb emplace_back
#define pb push_back
#define ft first
#define sd second
#define bg begin()
#define ed end()
#define sz(x) (int)(x.size())
#define alls(x) x.bg, x.ed
#define rep(i, l, r) for (int i = (l); i <= (int)(r); ++i)
#define re(i, l, r) rep(i, (l), (r) - 1)
#define per(i, r, l) for (int i = (r); i >= (int)(l); --i)
#define er(i, r, l) per(i, (r) - 1, (l))
#define repp(i, v) for (auto i : v)
#define i64 long long
#define ld long double
using namespace std;
const int inf = 1E9;
const i64 INF = 1E15;
#define int i64
const int N = 505;
int n, m, dis[N][N], cnt[N], ans[N][N], a[N * N], b[N * N], l[N * N];
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
rep(i, 1, n) rep(j, 1, n) dis[i][j] = (i == j ? 0 : INF);
rep(i, 1, m) {
cin >> a[i] >> b[i] >> l[i];
dis[a[i]][b[i]] = dis[b[i]][a[i]] = l[i];
}
rep(k, 1, n) rep(i, 1, n) rep(j, 1, n) {
if (k == i || k == j || i == j) continue;
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
rep(s, 1, n) {
fill(cnt + 1, cnt + 1 + n, 0);
rep(i, 1, m) {
if (dis[s][a[i]] + l[i] == dis[s][b[i]]) ++cnt[b[i]];
if (dis[s][b[i]] + l[i] == dis[s][a[i]]) ++cnt[a[i]];
}
rep(t, 1, n) rep(p, 1, n) {
if (dis[s][p] + dis[p][t] == dis[s][t])
ans[s][t] += cnt[p];
}
}
rep(i, 1, n) rep(j, i + 1, n) cout << ans[i][j] << ' ';
return 0;
}
标签:cnt,int,题解,rep,CF416E,枚举,dis,define
From: https://www.cnblogs.com/CTHOOH/p/18131787