26 LCA模拟赛3T2 连边 题解

连边

题面

给定一张初始 \(n\) 个点,没有边的图。

给定 \(m\) 表示有 \(m\) 个时刻,第 \(i\) 个时刻会将 \(gcd(a,b) = m - i + 1\) 某些点连起来。

\(q\) 个询问,每次询问给定 \(x, y\),你需要回答 \(x, y\) 最早在什么时刻连通

\(1 \le n,q \le 10^5, 1 \le m \le n\)

题解

这个暴力思路就是直接模拟。

考虑优化,我们可以对原来加的边进行一个等价变形:每个时刻将 \(i\)\(i\) 的倍数连边,边权为 \(i\) ,连通性是一样的。

这样,边数最多只有 \(O(n \log n)\)

我们对这些边按照边权从小到大加入到图中,用并查集维护连通性,从而构造出一棵最小生成树。

然后直接倍增查询路径最大边权即为答案。

时间复杂度 \(O(n \log n)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <set>

using namespace std;

namespace michaele {

    const int N = 1e5 + 10, M = N << 1;

    int n, m, q;
    int h[N], ver[M], ne[M], e[M], tot;
    int anc[N], f[N][23], g[N][23], dep[N];

    void add (int x, int y, int z) {
        ver[ ++ tot] = y;
        ne[tot] = h[x];
        h[x] = tot;
        e[tot] = z;
    }

    int fin (int x) {
        return x == anc[x] ? x : anc[x] = fin (anc[x]);
    }

    void dfs (int x, int fa) {
        for (int i = h[x]; i; i = ne[i]) {
            int y = ver[i];
            if (y == fa) continue;
            f[y][0] = x;
            g[y][0] = e[i];
            dep[y] = dep[x] + 1;
            dfs (y, x);
        }
    }

    int query (int x, int y) {
        if (dep[x] < dep[y]) swap (x, y);
        int res = 1;
        for (int i = 20; i >= 0; i --) {
            if (dep[f[x][i]] >= dep[y]) {
                res = max (res, g[x][i]);
                x = f[x][i];
            }
        }
        if (x == y) return res;
        for (int i = 20; i >= 0; i --) {
            if (f[x][i] != f[y][i]) {
                res = max ({res, g[x][i], g[y][i]});
                x = f[x][i];
                y = f[y][i];
            }
        }
        res = max ({res, g[x][0], g[y][0]});
        return res;
    }

    void solve () {
        cin >> n >> m >> q;
        for (int i = 1; i <= n; i ++) anc[i] = i;
        
        for (int i = 1; i <= m; i ++) {
            int k = m - i + 1;
            for (int j = k * 2; j <= n; j += k) {
                int x = fin (k), y = fin (j);
                if (x != y) {
                    anc[x] = y;
                    add (k, j, i);
                    add (j, k, i);
                }
            }
        }

        dep[1] = 1;
        dfs (1, 0);
        for (int j = 1; j <= 19; j ++) {
            for (int i = 1; i <= n; i ++) {
                if (!f[i][j - 1]) continue;
                f[i][j] = f[f[i][j - 1]][j - 1];
                g[i][j] = max (g[i][j - 1], g[f[i][j - 1]][j - 1]);
            }
        }
        for (int i = 1; i <= q; i ++) {
            int x, y;
            cin >> x >> y;
            if (x == y) {
                cout << 0 << endl;
                continue;
            }
            cout << query (x, y) << endl;
        }
    }
}


int main () {

    ios :: sync_with_stdio (0);
    cin.tie (0);
    cout.tie (0);

    michaele :: solve ();

    return 0;
}
posted @ 2025-10-16 15:03  michaele  阅读(2)  评论(0)    收藏  举报