[NOIP2013]货车运输

13 篇文章 0 订阅
6 篇文章 0 订阅
题目描述

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


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

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


输出

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


样例输入
4 3 
1 2 4 
2 3 3 
3 1 1 
1 3 
1 3

1 4 


样例输出
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。



题解:倍增LCA

先求出图中的最大生成树,两点间路径上的最小边(即瓶颈边)的权值为答案

f[i][j]记录节点i的第2^j个祖先,g[i][j]记录节点到第2^j个祖先上的瓶颈边;

在倍增法求LCA的过程中同步记录路径最小值即可。



#include<iostream>  
#include<cstring>  
#include<cstdlib>  
#include<cstdio>  
#include<cmath>  
#include<algorithm>  
using namespace std;  
const int INF=0x3f3f3f3f;  
const int N=10010;  
const int M=50010;  
const int LOG=25;  
  
    
int n, m, q, ans;  
void Getin( int &shu ) {  
    char c; int f=1; shu=0;  
    for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1;  
    for( ; c>='0' && c<='9'; c=getchar() ) shu=shu*10+c-'0';  
    shu*=f;  
}  
    
    
int fir[N], ecnt;  
struct node{ int e, w, next; }edge[N<<1];  
void Link( int s, int e, int w ) {  
    edge[++ecnt].e=e; edge[ecnt].w=w;  
    edge[ecnt].next=fir[s]; fir[s]=ecnt;  
    edge[++ecnt].e=s; edge[ecnt].w=w;  
    edge[ecnt].next=fir[e]; fir[e]=ecnt;  
}  
  
  
struct nodes{ int s ,e, w; }road[M];  
bool cmp( nodes a, nodes b ) { return a.w>b.w; }  
    
  
int h[N], rt[N]; 
int Root( int x ) { while( rt[x] ) x=rt[x]; return x; } 
void Kruskal() {  
    sort( road+1, road+m+1, cmp );  
    for( int i=1; i<=m; i++ ) {  
        int s1=road[i].s, t1=Root(s1);  
        int s2=road[i].e, t2=Root(s2);  
        if( t1!=t2 ) {  
            Link( s1, s2, road[i].w );  
            if( h[t1]<h[t2] ) rt[t1]=t2;  
            else rt[t2]=t1;  
            if( h[t1]==h[t2] ) h[t1]++;  
        }  
    }  
}  
  
  
int fa[N][LOG+5], g[N][LOG+5], dep[N];  
void DFS( int r, int f, int d ) {  
    dep[r]=d;  
    for( int i=fir[r]; i; i=edge[i].next )  
        if( edge[i].e!=f ) {  
            fa[edge[i].e][0]=r; 
            g[edge[i].e][0]=edge[i].w; 
            DFS( edge[i].e, r, d+1 );  
        }  
}  
    
void Build() {  
    for( int i=1; i<=n; i++ )  
        if( !rt[i] ) DFS( i, -1, 1 );  
    for( int j=1; j<=LOG; j++ )  
        for( int i=1; i<=n; i++ ) {  
            fa[i][j]=fa[ fa[i][j-1] ][j-1];  
            g[i][j]=min( g[i][j-1], g[ fa[i][j-1] ][j-1] );  
        }  
}  
    
    
int getk( int r ,int k ) {  
    for( int i=0; i<=LOG; i++ )  
        if( k&( 1<<i ) ) {  
            ans=min( ans, g[r][i] );  
            r=fa[r][i];  
        }  
    return r;  
}  
int getd( int r, int d ) { return getk( r, dep[r]-d ); }  
    
    
int p1, p2;  
int LCA_Find() {  
    ans=INF;  
    if( dep[p1]<dep[p2] ) swap( p1, p2 );   
    p1=getd( p1, dep[p2] );  
    if( p1==p2 ) return ans;  
    for( int i=LOG; i>=0; i-- )  
        if( fa[p1][i]!=fa[p2][i] ) {  
            ans=min( ans, min( g[p1][i], g[p2][i] ) );  
            p1=fa[p1][i]; p2=fa[p2][i];  
        }  
    return min( ans, min( g[p1][0], g[p2][0] ) );   
}  
    
    
int main() {  
    Getin(n); Getin(m);  
    for( int i=1; i<=m; i++ )  
        Getin( road[i].s ), Getin( road[i].e ), Getin( road[i].w );  
        
    Kruskal();  
    Build();  
      
    Getin(q);  
    for( int i=1; i<=q; i++ ) {  
        Getin( p1 ); Getin( p2 ); 
        if( Root(p1)!=Root(p2) ) printf( "-1\n" ); 
        else printf( "%d\n", LCA_Find() );  
    }  
    return 0;  
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值