LCA->火车运输(洛谷1967)

题目链接:https://www.luogu.com.cn/problem/P1967

题意:n个点,m条边的图。q个询问,问u->v之间权值最小的边权值是多少(不要求走最短路径,只要经过路径上经过的最小的边的权值最大)。

分析:如果两点间有路径,那么优先走边权更大的边,所以先利用最大生成树算法构建树。在构建了树之后,构建lca。对于每个点对u, v,找出他们的lca,并记录每个点到lca的路径上权值最小的边权值。


如果是森林,在dfs入口的地方写错了,起点应该是i。
不一定要用vis数组判定是否访问过,也可以用depth == -1。
大顶堆是less, <号。小顶堆是>, greater, >号。

void solve(){
    int n, m;
    cin >> n >> m;

    priority_queue<Edge, vector<Edge>, less<Edge>> pq;
    for (int i = 1; i <= m; ++i){
        Edge t;
        cin >> t;
        pq.emplace(t);
    }
    DisjointSet dsu(n + 1);
    vector<vector<pair<int, int>>> al(n + 1);
    while (!pq.empty()){
        auto[u, v, w] = pq.top();
        pq.pop();
        if (dsu.isSameSet(u, v)){
            continue;
        }
        dsu.unionSet(u, v);
        al[u].emplace_back(v, w);
        al[v].emplace_back(u, w);
    }


    int p = upper_bound(pow2_values.begin(), pow2_values.end(), n) - pow2_values.begin() - 1;
    vector<vector<int>> dp(n + 1, vector<int> (p + 1));
    vector<int> depth(n + 1, -1);
    vector<vector<int>> weight(n + 1, vector<int> (p + 1, 0x3f3f3f3f));
    vector<int> vis(n + 1);
    function<void(int, int, int)> dfs = [&](int u, int p, int d){
        depth[u] = d;
        dp[u][0] = p;
        vis[u] = 1;
        for (int i = 1; (1 << i) <= d; ++i){
            dp[u][i] = dp[dp[u][i - 1]][i - 1];
            weight[u][i] = min(weight[u][i - 1], weight[dp[u][i - 1]][i - 1]);
        }
        for (const auto& [v, w] : al[u]){
            if (v != p && vis[v] == false){
                weight[v][0] = w;
                dfs(v, u, d + 1);
            }
        }
    };

    for (int i = 1; i <= n; ++i){
        if (!vis[i]){
            dfs(i, 0, 1);
        }
    }

    auto cal = [&](int u, int v) -> int{
        int res = int(2e9);
        if (depth[u] < depth[v]){
            swap(u, v);
        }
        for (int i = p; i >= 0; --i){
            if (depth[u] - (1 << i) >= depth[v]){
                res = min(res, weight[u][i]);
                u = dp[u][i];
            }
        }
        if (u == v){
            return res;
        }
        for (int i = p; i >= 0; --i){
            if (dp[u][i] != dp[v][i]){
                res = min({res, weight[u][i], weight[v][i]});
                u = dp[u][i];
                v = dp[v][i];
            }
        }
        //!注意,此时的u和v是祖先下面一层的节点
        res = min({res, weight[u][0], weight[v][0]});
        return res;
    };

    int q;
    cin >> q;
    while (q --){
        int u, v;
        cin >> u >> v;
        if (dsu.isSameSet(u, v) == false){
            cout << -1 << '\n';
            continue;
        }
        cout << cal(u, v) << '\n';
    }

}
posted @   _Yxc  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示