【NOIP2013】Day1T3 货车运输

9 篇文章 0 订阅
8 篇文章 0 订阅

货车运输


  • Description

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

  • Input Format

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

  • Output Format

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

  • Sample Input

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

  • Sample Output

3
-1
3

  • Hint

对于 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。


  • 分析

有一种东西叫做最小瓶颈路,是指在两个结点之间求一条最长边最短的路径。而题目要求的是结点间最短边最长,我们可以用最小瓶颈路的方法求解。
首先,对于30%的数据,可以直接暴力Spfa求出每对结点的最短边。
其次,考虑一个应该算是贪心的东西,最终答案一定是在最大生成树上(证明:假设完全不在最大生成树上,那么可以在最大生成树上找到一条路径使得最长边最短;若一部分在最大生成树上且最短边在内,则答案相等,最短边不在内,则在最大生成树上的边都比这条最短边大)。所以我们可以建一颗最大生成树,然后找出每对结点间的路径。
然后,我们发现如果对于每个询问直接跑一遍树求结点间最短边,复杂度为 O(mlogmqn) ,只能处理60%的数据。因为是树上的问题,我们可以用树上倍增的方法来维护最短边。对于每个询问找出最近公共祖先,在寻找的同时处理答案即可,复杂度降为 O(mlogmqlogn)


#include <cstdio>
#include <algorithm>
using namespace std;
int tot,n,m,q,u,v,last[10001],father[10001],dep[10001],fa[10001][21],val[10001][21];
struct info{
    int to,next,val;
}e[1000000];
struct data{
    int u,v,w;
}edge[50001];
void addline(int u,int v,int c){
    e[++tot].to=v; e[tot].next=last[u]; last[u]=tot; e[tot].val=c;
    e[++tot].to=u; e[tot].next=last[v]; last[v]=tot; e[tot].val=c;
}
inline bool cmp(const data&a,const data&b){
    return a.w>b.w;
}
int getfather(int x){
    return x==father[x]?x:father[x]=getfather(father[x]);
}
void getdeep(int u,int father){
    fa[u][1]=father;
    for (int i=2;i<=20;i++){
        fa[u][i]=fa[fa[u][i-1]][i-1];
        val[u][i]=min(val[fa[u][i-1]][i-1],val[u][i-1]);
    }
    dep[u]=dep[father]+1;
    for (int i=last[u];i;i=e[i].next){
        if (father==e[i].to) continue;
        val[e[i].to][1]=e[i].val;
        getdeep(e[i].to,u);
    }
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    sort(edge+1,edge+1+m,cmp);
    for (int i=1;i<=m;i++){
        int fu=getfather(edge[i].u),fv=getfather(edge[i].v);
        if (fu!=fv){
            father[fu]=fv;
            addline(edge[i].u,edge[i].v,edge[i].w);
            if (tot==2*(n-1)) break;
        }
    }
    getdeep(1,0);
    for (scanf("%d",&q);q;q--){
        scanf("%d%d",&u,&v); 
        if (getfather(u)!=getfather(v)){
            printf("-1\n");
            continue;
        }
        int minu=1<<30,minv=1<<30;
        if (dep[u]<dep[v]) swap(u,v);
        for (int i;dep[u]!=dep[v];u=fa[u][i]){
            for (i=20;dep[fa[u][i]]<dep[v];i--);
            minu=min(minu,val[u][i]);
        }
        for (int i;u!=v;u=fa[u][i],v=fa[v][i]){
            for (i=20;fa[u][i]==fa[v][i] && i>1;i--);
            minu=min(minu,val[u][i]);
            minv=min(minv,val[v][i]);
        }
        printf("%d\n",min(minu,minv));
    }
    fclose(stdin); fclose(stdout);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值