Floyd-Warshall
Floyd-Warshall
给定\(n\)个点,\(m\)条边的无向图,\(q\)次询问,每次询问给定\(u,v\),询问两点之间最短路的长度(边权为\(1\))
\(1 \leq n, q \leq 1e5,0 < m - n < 100\)
题解
- 容易发现该图为稀疏图
- 所以我们考虑先建一颗生成树,那么最多会有\(100\)条非树边,即最多不超过\(200\)个点
- 我们考虑对这\(200\)个非树边上的点进行\(bfs\),求出每个点到到其他所有点的最短路,\(O(200(n + m))\)
- 那么对于一个询问\(u,v\)来说,要么\(u\)到\(v\)走的是树上最短路径,要么经过一个非树边上的点作为中转(类似\(floyd\))
- 所以对于每个询问,我们可以\(O(200)\)暴力枚举所有非树边上的点,设非树边上点为\(i\),则经过\(i\)的最短路径为\(dis[i][u] + dis[i][v]\)
- 如果走树上路径,求\(lca\)后简单容斥即可
- 那么每次询问的答案就是两种情况中的最小值
- 注意:\(n = 1\)的时候特判即可
// https://qoj.ac/problem/3711
const int N = 1e5 + 1010, M = 4e5 + 10;
int n, m, q, fa[N], dep[N], dp[N][22], st[N];
int mp[N], idx, dis[210][N];
vector<array<int, 3>> edge;
vector<int> g[N], G[N];
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void dfs(int u, int par)
{
dep[u] = dep[par] + 1;
dp[u][0] = par;
for (int i = 1; i <= 20; ++i)
dp[u][i] = dp[dp[u][i - 1]][i - 1];
for (auto v : g[u])
{
if (v == par)
continue;
dfs(v, u);
}
}
int lca(int u, int v)
{
if (dep[u] < dep[v])
swap(u, v);
for (int i = 20; i >= 0; --i)
{
if (dep[dp[u][i]] >= dep[v])
u = dp[u][i];
}
if (u == v)
return u;
for (int i = 20; i >= 0; --i)
{
if (dp[u][i] != dp[v][i])
{
u = dp[u][i];
v = dp[v][i];
}
}
return dp[u][0];
}
void bfs(int id, int st)
{
dis[id][st] = 0;
vector<int> vis(n + 10);
queue<int> q;
q.push(st);
vis[st] = true;
while (q.size())
{
int u = q.front();
q.pop();
for (auto v : G[u])
{
if (!vis[v])
{
vis[v] = true;
dis[id][v] = dis[id][u] + 1;
q.push(v);
}
}
}
}
void solve()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; ++i)
fa[i] = i;
for (int i = 1; i <= m; ++i)
{
int u, v;
cin >> u >> v;
edge.push_back({u, v, i});
G[u].push_back(v);
G[v].push_back(u);
}
int rt = 0;
for (auto [x, y, id] : edge)
{
int fx = find(x), fy = find(y);
if (fx != fy)
{
fa[fx] = fy;
g[x].push_back(y);
g[y].push_back(x);
rt = x;
st[id] = true;
}
}
dfs(rt, 0);
for (int i = 1; i <= m; ++i)
{
if (!st[i])
{
auto [u, v, id] = edge[i - 1];
if (!mp[u])
{
mp[u] = ++idx;
bfs(idx, u);
}
if (!mp[v])
{
mp[v] = ++idx;
bfs(idx, v);
}
}
}
while (q--)
{
int u, v;
cin >> u >> v;
if (n == 1)
{
cout << 0 << endl;
continue;
}
int LCA = lca(u, v);
int ans = dep[u] + dep[v] - 2 * dep[LCA];
for (int i = 1; i <= idx; ++i)
ans = min(ans, dis[i][u] + dis[i][v]);
cout << ans << endl;
}
}