【luogu4320】道路相遇 (圆方树 + LCA)

Description

​ 给你一张\(~n~\)个点\(~m~\)条边的无向图,保证无重边无自环, 共\(~q~\)组询问求\(~x~\)\(~y~\)的路径上必经的点数。

Solution

​ 建出圆方树后, 不难发现答案所求就是\(~x~\)\(~y~\)的路径上的圆点个数, 而圆方树拥有的优秀性质就是相邻点对一圆一方,所以圆点个数就是 树上路径长度的\(~1/2~ + 1~\), 树上路径长度可以简单地由\(~dep_x,~dep_y, ~dep_{lca(x, ~y)}~\)求得。

Code

#include<bits/stdc++.h>
#define Set(a, b) memset(a, b, sizeof (a))
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
#define Travel(i, u, G) for(int i = G.beg[u], v = G.to[i]; i; i = G.nex[i], v = G.to[i])
using namespace std;

inline int read() {
    int x = 0, p = 1; char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x *= p;
}

template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

inline void File() {
#ifndef ONLINE_JUDGE
    freopen("P4320.in", "r", stdin);
    freopen("P4320.out", "w", stdout);
#endif
}

const int N = 1e6 + 10, M = N << 1;
struct edge { 
    int e = 1, beg[N], nex[M], to[M]; 
    inline void add(int x, int y) { to[++ e] = y, nex[e] = beg[x], beg[x] = e; }
} G1, G2;
int n, m, u, v, dfn[N], low[N];
int cnt, clk, F[21][N], dep[N];
stack<int> S;

inline void tarjan(int u, int f) {
    dfn[u] = low[u] = ++ clk, S.push(u);
    Travel(i, u, G1) if (v != f) {
        if (!dfn[v]) {
            tarjan(v, u), chkmin(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                F[0][++ cnt] = u, G2.add(u, cnt);
                while (!S.empty()) {
                    int x = S.top(); S.pop();
                    F[0][x] = cnt, G2.add(cnt, x);
                    if (x == v) break;
                }   
            }
        } else chkmin(low[u], dfn[v]);
    }
}

inline void dfs(int u, int las) {
    Travel(i, u, G2) dep[v] = dep[u] + 1, dfs(v, u);
}

inline int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    Forr(i, 20, 0) if (dep[F[i][x]] >= dep[y]) x = F[i][x];
    if (x == y) return y;
    Forr(i, 20, 0) if (F[i][x] ^ F[i][y]) x = F[i][x], y = F[i][y];
    return F[0][x];
}


int main() {
    File();
    cnt = n = read(), m = read();
    For(i, 1, m) u = read(), v = read(), G1.add(u, v), G1.add(v, u);
    For(i, 1, n) if (!dfn[i]) tarjan(i, 0);
    
    dfs(1, 0);
    For(j, 1, 20) For(i, 1, n) F[j][i] = F[j - 1][F[j - 1][i]];

    for (int Case = read(); Case --;) { 
        u = read(), v = read();
        int ans = (dep[u] + dep[v] - (dep[lca(u, v)] << 1)) / 2 + 1;
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LSTete/p/9495230.html
lca
今日推荐