首页 > 其他分享 >洛谷 P1967 货车运输

洛谷 P1967 货车运输

时间:2023-03-23 22:23:07浏览次数:69  
标签:路径 洛谷 min int ans 货车运输 fa lca P1967

P1967 NOIP2013 提高组] 货车运输 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这个题目算是lca的稍微拓展吧。主要思考方向应该是很明显的。就是考虑一条路径上权值最小的边。

1.首先这个题他不一定是联通图。其次我们发现是要在一个联通块内才能有有路径。还有题目不保证无环,所以我们要先建树。可以发现如果两个点之间有权值更大的路径是不会走权值小的路径的,比如题目中的例子。所以可以考虑建最大生成树。

void kruskal(){
    for(int i = 1; i <= n; i++){
        fa[i] = i;
    }
    sort(e + 1, e + 1 + m, [&](const edge &x, const edge &y){    
        return x.w > y.w;
    });
    for(int i = 1; i <= m; i++){
        auto [u, v, w] = e[i];
        int fau = find(u), fav = find(v);
        if(fau != fav){
            fa[fav] = fau;
            b[u].push_back({v, w});
            b[v].push_back({u, w});
        }
    }
}   

 

2.建好树之后如何思考呢?我们需要求一条路径上所有边权值的最小值,这个很好处理,就在lca的时候的再开一个数组记录以下就好。

for(int i = 1; i <= 20; i++){
    for(int j = 1; j <= n; j++){
        f[j][i] = f[f[j][i - 1]][i - 1];
        w[j][i] = min(w[j][i - 1], w[f[j][i - 1]][i - 1]);
    }
}

 

 

3.最后的查询就直接求两个点lca就行,求lca每次往上跳的时候求一下该段路径的最小值。注意两个点不在一个联通块的情况。

int lca(int x, int y){
    if(find(x) != find(y)) return -1;
    if(d[x] < d[y]) swap(x, y);
    int z = d[x] - d[y];
    int ans = inf;
    for(int j = 0; j <= 20 && z; j++, z /= 2){
        if(z & 1){
            ans = min(ans, w[x][j]);
            x = f[x][j];
        }
    }
    if(x == y){
        return x;
    }
    for(int i = 20; i >= 0; i--){
        if(f[x][i] != f[y][i]){
            ans = min({ans, w[x][i], w[y][i]});
            x = f[x][i];
            y = f[y][i];
        }
    }
    ans = min({ans, w[x][0], w[y][0]});
    return (ans == inf ? -1 : ans);
}

 

4.完整代码

#include<bits/stdc++.h>
using namespace std;
​
​
const int N = 5e4 + 100;
const int inf = 1 << 30;
struct edge{
    int u, v, w;
}e[N];
vector<pair<int,int>>b[N];
int fa[N];
int n, m;
int d[N], f[N][21], w[N][21];
bool st[N];
​
​
void kruskal();
int find(int);
int lca(int,int);
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
​
​
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        int u, v, w;
        cin >> u >> v >> w;
        e[i] = {u ,v, w};
    }
    kruskal();
    function<void(int,int)> dfs = [&](int u, int fa){
        st[u] = true;
        d[u] = d[fa] + 1; 
        f[u][0] = fa;
        for(auto [v, b_w] : b[u]){
            if(st[v]) continue;
            w[v][0] = b_w;
            dfs(v, u);
        }
    };
    for(int i = 1; i <= n; i++){
        if(!st[i]){
            dfs(i, 0);
            w[i][0] = 1 << 30;
        }
    }
    for(int i = 1; i <= 20; i++){
        for(int j = 1; j <= n; j++){
            f[j][i] = f[f[j][i - 1]][i - 1];
            w[j][i] = min(w[j][i - 1], w[f[j][i - 1]][i - 1]);
        }
    }
    int q;
    cin >> q;
    while(q--){
        int x, y;
        cin >> x >> y;
        cout << lca(x, y) << '\n';
    }
    return 0;
}
void kruskal(){
    for(int i = 1; i <= n; i++){
        fa[i] = i;
    }
    sort(e + 1, e + 1 + m, [&](const edge &x, const edge &y){    
        return x.w > y.w;
    });
    for(int i = 1; i <= m; i++){
        auto [u, v, w] = e[i];
        int fau = find(u), fav = find(v);
        if(fau != fav){
            fa[fav] = fau;
            b[u].push_back({v, w});
            b[v].push_back({u, w});
        }
    }
}   
int find(int x){
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}
int lca(int x, int y){
    if(find(x) != find(y)) return -1;
    if(d[x] < d[y]) swap(x, y);
    int z = d[x] - d[y];
    int ans = inf;
    for(int j = 0; j <= 20 && z; j++, z /= 2){
        if(z & 1){
            ans = min(ans, w[x][j]);
            x = f[x][j];
        }
    }
    if(x == y){
        return x;
    }
    for(int i = 20; i >= 0; i--){
        if(f[x][i] != f[y][i]){
            ans = min({ans, w[x][i], w[y][i]});
            x = f[x][i];
            y = f[y][i];
        }
    }
    ans = min({ans, w[x][0], w[y][0]});
    return (ans == inf ? -1 : ans);
}
/*
    核心任务求得就是一条路径的最小值
    可以先建立一颗最大生成树,显而易见就是两个点之间如果能通过大边联系时不会通过小边的
    然后再dp把 w[i][j] = min(w[i][j - 1], w[fa[i][j - 1]][j - 1]);
    最后lca就是求某一段路径上的w
*/
 

标签:路径,洛谷,min,int,ans,货车运输,fa,lca,P1967
From: https://www.cnblogs.com/silky----player/p/17249733.html

相关文章

  • 洛谷 P5979 [PA2014]Druzyny
    简要题意有\(n\)个人,把他们划分成尽可能多的区间,其中第\(i\)个人要求它所在的区间长度大于等于\(c_i\),小于等于\(d_i\),求最多的区间数量以及如此划分的方案数。数......
  • 洛谷P1115 最大子段和
    题目传送门题目描述给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。输入格式第一行是一个整数,表示序列的长度 n。第二行有 n 个整数,第......
  • 洛谷 P3758 [TJOI2017]可乐
    https://www.luogu.com.cn/problem/P3758给定一张图。一个机器人在1号点,每次它可以选择留在原地,沿一条边行走一次,自爆。机器人自爆后无法进行任何操作,求t时间内它所有可......
  • 【洛谷】P4590 [TJOI2018]游园会(dp套dp)
    原题链接题意对于一个长度为\(n\)的仅由\(N,O,I\)组成且不包含字串\(NOI\)的字符串\(S\),其与一个给定的长度为\(K\)的字符串的最长公共子序列为\(LCS\)。求出......
  • 【洛谷】P2150 [NOI2015] 寿司晚宴(状压dp+根号分治)
    原题链接题意有序列\(2,3,4\cdotsn\),对于序列中的每一个数,它可以被放入两个集合中的任意一个,或者不选。最后需要满足两个集合间的数两两互质(集合内部的数不需要满足互......
  • 【洛谷】P5904 [POI2014]HOT-Hotels(长链剖分)
    原题链接题意给出一棵有\(n\)个点的树,求有多少组点\((i,j,k)\)满足\(i,j,k\)两两之间的距离都相等。\((i,j,k)\)与\((i,k,j)\)算作同一组。\(1\len\le10^5\)......
  • 洛谷炸了,这次连日爆都没有了
    很讨厌,什么人攻击这么勤快,禁国外ip还防不了......
  • 【洛谷】P2480 [SDOI2010]古代猪文
    原题链接题意求:\[g^{\sum_{d|n}\binom{n}{d}}\mod999911659\]\(n,g\leq10^9\)。思路:因为\(999911659\)是质数,由欧拉定理的推论,可以得到:\[g^{\sum_{d|n}\bino......
  • 【洛谷】P2257 YY的GCD(莫比乌斯反演)
    原题链接题意\(T\)组询问,每次询问求:\[\sum_{i=1}^{n}\sum_{i=1}^{m}[\gcd(i,j)\inprime]\]\(T=10^4,n,m\leq10^7\)。思路不难想到枚举质数,将原式化简为:\[\sum......
  • 【洛谷】P4139 上帝与集合的正确用法(扩展欧拉定理)
    原题链接题意求:\[2^{2^{2^{\ldots}}}\modp\]可以证明这个式子一定为一个常数。\(1\leqp\leq10^7\)思路根据扩展欧拉定理,可以得到:\[2^{2^{2^{\ldots}}}\equi......