洛谷 P1967 [NOIP2013 提高组] 货车运输

题意:n个点m条边的图,q个询问,每个询问指定两个点u和v。问从u到v的路径中,最小权重的最大值最多是多少。

思路:step1,因为是图,所以路径有很多。这里基于贪念的思想,优先选择权重大的边走,找出一条最合适的路径。 所以直接对图构建最大生成树,让每个连通块的路径都是最大路径。 step2,构建最大生成子树后,开始处理查询。对于每个查询u和v,如果u和v存在路径,那么它们一定在同一个生成树中,为了避免每次查询都要一次遍历,可以考虑florida最短路径算法。 但是florida算法的n不能超过250。所以这里考虑使用一个lca算法,在更新祖先的时候,记录一下当前点到该祖先的最小权重值。在查询祖先的时候,就可以实现log(n)的复杂度来查找每一次询问。

class DisjointSet{
private:
    vector<int> fa_;
    int sz_, num_sets_;


    vector<int> set_size_;
    vector<int> dist_;
    vector<vector<int>> elements_;

    //dist依赖setsize(当两个节点合并时,dist_[x] = set_size_[y],elementssetsize不依赖其他任何。
    bool use_dist_ = false;
    bool use_set_size_ = true;
    bool use_elements_ = false;


    void updateDist(int x){
        if (fa_[x] == x){
            return;
        }
        int par = fa_[x];
        fa_[x] = findSet(par);
        dist_[x] += dist_[par];
    }

public:
    DisjointSet(int sz): sz_(sz), num_sets_(sz){
        fa_.resize(sz_);
        iota(fa_.begin(), fa_.end(), 0);

        assert(use_dist_ == use_set_size_ || (use_set_size_ == true));  //节点合并时dist_的值更新必须用到set_size_,而set_size_不依赖dist_
        if (use_set_size_){
            set_size_.resize(sz_);
            fill(set_size_.begin(), set_size_.end(), 1);
        }
        if (use_dist_){
            dist_.resize(sz_);
        }
        if (use_elements_){
            elements_.resize(sz_);
            for (int i = 0; i < sz_; ++i){
                elements_[i].emplace_back(i);
            }
        }
    }

    int findSet(int x){
        if (fa_[x] == x){
            return x;
        }
        else{
            if (use_dist_){
                updateDist(x);
                return fa_[x];
            }
            return fa_[x] = findSet(fa_[x]);
        }
    }

    int getSetSize(int x){
        assert(use_set_size_ == true);
        return set_size_[findSet(x)];
    }

    int getDist(int x){
        assert(use_dist_ == true);
        updateDist(x);
        return dist_[x];
    }

    int countSets(){
        return num_sets_;
    }

    vector<int> getSetElements(int x){
        assert(use_elements_ == true);
        return elements_[findSet(x)];
    }

    bool isSameSet(int x, int y){
        return findSet(x) == findSet(y);
    }

    bool unionSet(int x, int y){
        x = findSet(x);
        y = findSet(y);
        if (x == y){
            return false;
        }
        fa_[x] = y;
        num_sets_ --;
        if (use_elements_){
            elements_[y].insert(elements_[y].end(), elements_[x].begin(), elements_[x].end());
            elements_[x].clear();
        }
        if (use_dist_){
            dist_[x] = set_size_[y];        //初始化x到y的距离
        }
        if (use_set_size_){
            set_size_[y] += set_size_[x];
        }
        return true;
    }
};


struct Edge{
    int u, v, w;
    friend istream& operator >>(istream& is, Edge& edge){
        is >> edge.u >> edge.v >> edge.w;
        return is;
    }
    bool operator <(const Edge& other) const{
        return w < other.w;
    }
};

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

    priority_queue<Edge, vector<Edge>, less<Edge>> pq;
    for (int i = 0; i < m; ++i){
        Edge t;
        cin >> t;
        pq.emplace(t);
    }

    vector<vector<pair<int, int>>> al(n + 1);
    DisjointSet dsu(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 k = upper_bound(pow2_values.begin(), pow2_values.end(), n) - pow2_values.begin() - 1;
    vector<vector<int>> dp(n + 1, vector<int>(k + 1));
    vector<int> depth(n + 1, -1);
    vector<vector<int>> weight(n + 1, vector<int>(k + 1, 0x3f3f3f3f));
    function<void(int, int, int)> lca = [&](int u, int p, int d){
        depth[u] = d;
        dp[u][0] = p;
        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 && depth[v] == -1){
                weight[v][0] = w;
                lca(v, u, d + 1);
            }
        }
    };

    for (int i = 1; i <= n; ++i){
        if (depth[i] == -1){
            lca(i, 0, 1);
        }
    }

    auto cal = [&](int u, int v) -> int{
        int res = 0x3f3f3f3f;
        if (depth[u] < depth[v]){
            swap(u, v);
        }
        for (int i = k; i >= 0; --i){
            if (depth[u] - pow2_values[i] >= depth[v]){
                res = min(res, weight[u][i]);
                u = dp[u][i];
            }
        }
        if (u == v){
            return res;
        }
        for (int i = k; 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];
            }
        }
        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";
        }
        else{
            cout << cal(u, v) << '\n';
        }
    }



}
posted @   _Yxc  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示