zoukankan      html  css  js  c++  java
  • 洛谷:P1967 货车运输

    原题地址:https://www.luogu.org/problemnew/show/P1902

    题目简述

    给定一个n个点m条边的无向带权图,每次询问2点u,v的联通情况,不联通则输出-1。
    如果联通,不妨将一条联通u,v的路径上的最小权值记为w,则该次询问输出所有可能的w中的最大值。
    共有q次询问。


    思路

    对于任意两点u&v,我们需要找出能使得w最大的一条最优路径。
    因此需要生成一个新图,使得原图中联通任意两点之间只存在一条能使得w最大的最优路径。
    因此这是一棵树……
    又因为要使w最大,应尽量选择边权大的边作为路径…… 然后就突然发觉:这不就是Kruskal算法的过程吗?只不过最小生成树优先选择边权小的边,此时优先选择边权大的。
    因此要求的新图就是一颗最大生成树……Kruskal可破。
    然后就是求任意两点LCA了。此处使用倍增,也方便维护某节点向树根爬的时候路上的最小权值。
    (用树链剖分+线段树维护也行…………)
    更具体的看代码注释。


    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define inf 1000000005
    struct Node {
        int u,v,w;//两点u&v以及边权
        bool operator < (const Node &b) const {
            return w<b.w;
        }
    };
    vector <Node> e[50005];//e[u]储存节点u相连的边集。
    priority_queue <Node> Q;//边权越大的优先级越高
    int fa[50005];//Kruskal的御用并查集,fa[u]代表u点所处集合
    bool vis[50005];//是否已经被dfs过程访问过
    int fas[50005][21],minw[50005][21],deep[50005];
    //fas[u][j]代表u点在所处树中的第2^j级父亲编号
    //minw[u][j]代表u点在所处树中至第2^j级父亲的路径上最小边权
    //deep[u]代表u点在所处树中深度
    int find(int x)//查找x所在集合编号
    {
        if (x==fa[x]) return x;
        else return fa[x]=find(fa[x]);//路径压缩
    }
    void uni(int a,int b) //合并a,b所在集合
    {
        fa[find(a)]=find(b);
    }
    
    void add(int u,int v,int w) //添加新图边
    {
        Node one;
        one.u=u;
        one.v=v;
        one.w=w;
        e[u].push_back(one);
        uni(u,v);
    }
    void dfs(int u,int f,int k)//dfs,u代表当前点,f为当前点父亲,k为深度
    {
        vis[u]=1;
        deep[u]=k;
        for (int i=0;i<e[u].size();i++) {
            if (e[u][i].v==f) continue;
            else {
                dfs(e[u][i].v,u,k+1);
                fas[e[u][i].v][0]=u;
                minw[e[u][i].v][0]=e[u][i].w;
            }
        }
    }
    int n,m,q;
    void Kruskal()
    {
        int linked=0;
        while(!Q.empty()&&linked<n-1) {//边数m可能少于n-1,因此需要注意Q是否为空
            Node now=Q.top();
            Q.pop();
            int a=now.u,b=now.v;
            if (find(a)==find(b)) continue;
            else {
                linked++;
                add(a,b,now.w);
                add(b,a,now.w);
            }
        }
    }
    int lca(int x,int y)//求x,y的lca
    {
        if (find(x)!=find(y)) return -1;//不在一个树里
        int ans=inf;
        if (deep[y] >deep[x]) swap(x,y);//较深的标记为x
        for(int i=20;i>=0;i--)//令x跳到与y相同高度
            if(deep[fas[x][i]]>=deep[y]){
                ans=min(ans,minw[x][i]);
                x=fas[x][i];
            }
        if (x==y) return ans;
        for(int i=20; i>=0; i--)//让x,y一起跳到lca节点下方
            if(fas[x][i]!=fas[y][i]){
                ans=min(ans,min(minw[x][i],minw[y][i]));
                x=fas[x][i]; 
                y=fas[y][i];
            }
        ans=min(ans,min(minw[x][0],minw[y][0]));//统计最小边权
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
            fa[i]=i;//并查集预处理,各个点都处于自己所代表的集合
        for (int i=1;i<=m;i++) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            Node one;
            one.u=x;
            one.v=y;
            one.w=z;
            Q.push(one);//加入Kruskal御用队列Q
        }
        Kruskal();//最大生成树
        for (int i=1;i<=n;i++) {//倍增数组初始化&dfs
            if (!vis[i]) {
                dfs(i,0,1);
                fas[i][0]=i;
                minw[i][0]=inf;
            }
        }
        for (int i=1;i<=20;i++) {//倍增预处理
            for (int j=1;j<=n;j++) {
                fas[j][i]=fas[fas[j][i-1]][i-1];
                minw[j][i]=min(minw[j][i-1],minw[fas[j][i-1]][i-1]);
            }
        }
        scanf("%d",&q);
        for (int i=1;i<=q;i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            printf("%d
    ",lca(a,b));
        }
        return 0;
    }
    
  • 相关阅读:
    Running as a packaged application--- -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n
    Druid 专题
    java8中的localdate和localtime用法举例
    Spring处理跨域请求
    数据库版本管理工具Flyway——基础篇
    QQ邮箱开启SMTP服务的步骤
    QQ邮箱如何设置SMTP代理收发邮件
    SMTP协议简介
    Error:Execution failed for task ':app:dexDebug'. > com.android.ide.common.process.ProcessException
    Android客户端与服务器交互中的token
  • 原文地址:https://www.cnblogs.com/yyy2015c01/p/9718125.html
Copyright © 2011-2022 走看看