Floyd-Warshall

发布时间 2023-08-29 00:57:13作者: Zeoy_kkk

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