zoukankan      html  css  js  c++  java
  • NOIP2013 DAY2 T3火车运输

    传送门

    题目描述

    A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

    输入输出格式

    输入格式:

    输入文件名为 truck.in。

    输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

    路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。

    接下来一行有一个整数 q,表示有 q 辆货车需要运货。

    接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。

    输出格式:

    输出文件名为 truck.out。

    输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货

    车不能到达目的地,输出-1。

    输入输出样例

    输入样例#1: 复制
    4 3
    1 2 4
    2 3 3
    3 1 1
    3
    1 3
    1 4
    1 3
    输出样例#1: 复制
    3
    -1
    3

    说明

    对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;

    对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;

    对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

    这个问题叫最大瓶颈路径。

    结论: 最大瓶颈路径一定在最大生成树中。

    证明:

    反证法。如果最大瓶颈路径不存在与最大生成树中。这些不在最大生成树中的边会和最大生成树形成环。

    我们删掉环上最小的边,保留这一条边,会得到一棵新的更大的生成树。这与原来那棵树是最大生成树矛盾了

    所以这一题我们就先用kruskal求最小生成树,然后转化为求树上路径的最短边。

    对于求最短边,我们只要用求LCA的倍增里面加点东西就行了。

    我们设f[i][j]为i节点的第2^j的祖先,设dism[i][j]为从i节点到i的第2^j个节点的最小边长。

    然后只要在对f数组进行递推的过程中顺便递推dism数组就行。

    递推公式为:  dism[i][j] = min(dism[i][j-1],diam[f[i][j-1]][j-1])   。 

    递推前需要对dism数组进行初始化全设为最大值。

    之后在求,u,v两点的最近公共祖先时,随着点的跳跃二每部进行更新最小值就行了。

    另外,在用kruskal建最小生成树时,可能有一些点是不在树上的(因为这些点不连通),所以我们用一个belong数组记录那些点在树上,

    当询问时,我们就先判断要询问的两点同不同时在树上,如果不在,输出-1,否则进行求最近公共祖先的过程。

    下面是代码,有问题留言。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 10009
    #define M 50009
    using namespace std;
    
    int en,n;
    
    struct edge{                 //存最大生成树的图 
        int e,d;
        edge *next;
    }*v[N],ed[2*N];
    
    void add_edge(int s,int e,int d){
        en++;
        ed[en].next = v[s],v[s] = ed+en,v[s]->e =e;
        ed[en].d =d;
    }
    
    struct edg{                    //存原图 
        int s,e,d;
    }ek[M];
    
    int eni;
    void add_edgei(int s,int e,int d){
        eni++;
        ek[eni].s = s,ek[eni].e = e,ek[eni].d =d;
    }
    
    int fa[N];
    
    int getf(int now){                        //并查集 
        if(fa[now] == now)return now;
        else return fa[now] = getf(fa[now]);
    }
    
    bool operator <(const edg &a,const edg &b){        //重载运算符 
        return a.d > b.d;
    }
    
    int belong[N];
    
    void kruskal(){                        //建图 
        for(int a = 1; a <= n; a++)fa[a] =a;
        sort(ek+1,ek+eni+1);
        for(int i = 1; i <= eni; i++){
            int f1 = getf(ek[i].s);
            int f2 = getf(ek[i].e);
            if(f1 != f2){
                fa[f2] = f1;
                add_edge(ek[i].s,ek[i].e,ek[i].d);
                add_edge(ek[i].e,ek[i].s,ek[i].d);
                belong[ek[i].s] = 1;                //记录当前两点在树上 
                belong[ek[i].e] = 1;
            }
        }
    }
    
    int read(){                                //读入优化 
        int x = 0;
        char ch = getchar();
        while(ch < '0' || ch > '9')ch = getchar();
        while(ch >= '0' && ch <= '9'){
            x = x * 10 + ch -'0';
            ch = getchar();
        }
        return x;
    }
    
    int f[N][25],dism[N][25],deep[N];
    bool use[N];
    
    void dfs(int now,int dep){
        use[now] = true;
        deep[now] = dep;
        for(int k = 1; k <= 19; k++){
            int j = f[now][k-1];
            f[now][k] = f[j][k-1];
            dism[now][k] = min(dism[now][k-1],dism[j][k-1]);
        }
        for(edge *e = v[now];e;e=e->next)
          if(!use[e->e]){
              f[e->e][0] = now;
              dism[e->e][0] = e->d;
              dfs(e->e,dep+1);
          }
        use[now] = false;
    }
    
    
    int jump(int u,int step,int &minc){
        for(int k = 0; k <= 19; k++)
           if((step & (1 << k))){
                   minc = min(minc,dism[u][k]);
                   u = f[u][k];
           }
        return u;
    }
    
    int qlca(int u,int v){
        if(belong[u] != belong[v])return -1;        //u,v,两点不在树上 
        if(deep[u] < deep[v])swap(u,v);
        int minc = 100009;
        u = jump(u,deep[u]-deep[v],minc);
        for(int k = 19; k >= 0; k--)
           if(f[u][k] != f[v][k]){
                  minc = min(dism[v][k],min(minc,dism[u][k]));
                  u = f[u][k];
                  v = f[v][k];
           }
        if(u == v)return minc;
        else return min(minc,min(dism[u][0],dism[v][0]));
    }
    
    int main(){
        memset(dism,0x3f,sizeof(dism));
        int m;
        n = read(),m = read();
        for(int i = 1; i <= m; i++){
            int u = read(),v = read(),d = read();
            add_edgei(u,v,d);
        }
        kruskal();                
        f[1][0] = 1;
        dfs(1,0);
        int q = read();
        while(q--){
            int u = read(), v =read();
            printf("%d
    ",qlca(u,v));          //求u,v/路径的最小边 
        }
        return 0; 
    } 
  • 相关阅读:
    linux软件安装方式
    docker 安装 jenkins touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
    [ERR] Node goodsleep.vip:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
    Linux 常用命令 服务器间scp 用户 export 创建文件、软连接
    redis 安装 集群 主从 哨兵 docker
    WPF密码框中禁止复制、粘贴
    Application 统计在线人数
    【转义字符】HTML 字符实体&lt; &gt: &amp;等
    SQL语句统计每天的数据
    正则表达式计算代码数
  • 原文地址:https://www.cnblogs.com/bingdada/p/7735325.html
Copyright © 2011-2022 走看看