[NOIP2013]货车运输 D1 T3 kruscal最大生成树+树上倍增lca+rmq

题意: n个点,m条边,每条边有自己的限重,q个询问,询问满足火车从x到y的最大限重.

方法: kruscal最大生成树+树上倍增lca+rmq.

解析: 由于两个点间有可能有重边,而且我们要的是最大限重,所以选取前n-1条最大的边构成一棵树,在这棵树上进行操 

 作.求x到y的路径,则需要考虑x与y是否在一棵树上,不是则输出-1,在一棵树上,找出x与y的公共祖先的过程中采用

 rmq记录最大的最小边权.

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;
struct node
{
    int from ; 
    int to ;
    int val ;
    int next ;
};
node edge[100001] ;
node l[50001] ;
int head[10001] ;
int fa[10001][20] ;
int deep[10001] ;
int v[10001] ;
int sum[10001][20] ;
int f[10001] ;
int cnt , n , m , tot , pt;
int find(int x)
{
    if(f[x] == x) return x ;
    else
    {
        f[x] = find(f[x]) ;
        return f[x] ; 
    }
}
void init()
{
    memset(head , -1 , sizeof(head)) ;
    memset(sum , 0x3f , sizeof(sum)) ;
    cnt = 1 ;
    for(int i = 1 ; i <= n ; i++) f[i] = i ;
}
void dfs(int now , int f , int dep)
{
    v[now] = 1 ;
    deep[now] = dep ;
    for(int i = head[now] ; i != -1 ; i = edge[i].next)
    {
        int to = edge[i].to ;
        if(to == f) continue ;
        fa[to][0] = now ;
        sum[to][0] = edge[i].val ;
        dfs(to , now , dep + 1) ;
    }
}
void edgeadd(int from , int to , int val)
{
    edge[cnt].from = from ;
    edge[cnt].to = to ;
    edge[cnt].val = val ;
    edge[cnt].next = head[from] ;
    head[from] = cnt ++ ;
}
int cmp(node a , node b)
{
    return a.val > b.val ;
}
int lca(int x , int y)
{
    pt = 0x3f3f3f3f ;
    int re ;
    if(deep[x] < deep[y]) swap(x , y) ;
    for(int i = 18 ; i >= 0 ; i--)
    {
        if(deep[fa[x][i]] >= deep[y]&&fa[x][i]!=0) pt = min(sum[x][i] , pt) ,x = fa[x][i] ;
    }
    if(x == y) return x ;
    for(int i = 18 ; i >= 0 ; i--)
    {
        if(fa[x][i] != fa[y][i]&&fa[x][i]&&fa[y][i]) 
        {
            pt = min(sum[x][i] , pt) ;
            pt = min(sum[y][i] , pt) ;
            x = fa[x][i] , y = fa[y][i] ;
        }
    }
    pt = min(pt , min(sum[x][0] , sum[y][0])) ;
    return re ;
}
int main()
{
    scanf("%d%d" , &n , &m) ;
    init() ;
    for(int i = 1 ; i <= m ; i++)
    {
        scanf("%d%d%d" , &l[i].from , &l[i].to , &l[i].val) ;
    }
    sort(l + 1 , l + m + 1 , cmp) ;
    for(int i = 1 ; i <= m ; i++)
    {
        int x = l[i].from , y = l[i].to ;
        int fx = find(x) , fy = find(y) ;
        if(fx != fy)
        {
            tot ++ ;
            f[fx] = fy ;
            edgeadd(x , y , l[i].val) ;
            edgeadd(y , x , l[i].val) ;
            if(tot == n-1)
            {
                break ;
            }
        }   
    }
    for(int i = 1 ; i <= n ; i++)
    {
        if(!v[i]) dfs(i , 0 , 1) ;
    }
    for(int j = 0 ; (1 << j) <= n ; j++)
    {
        for(int i = 1 ; i <= n ; i++)
        {
            if(fa[fa[i][j-1]][j-1] != 0)
            {
                fa[i][j] = fa[fa[i][j-1]][j-1] ;
                sum[i][j] = min(sum[i][j-1] , sum[fa[i][j-1]][j-1]) ;
            }   
        }
    }
    int q ;
    scanf("%d" , &q) ;
    for(int i = 1 ; i <= q ; i++)
    {
        int x , y ;
        scanf("%d%d" , &x , &y) ;
        if(find(x) != find(y))
        {
            printf("-1\n") ;
            continue ; 
        }
        int t = lca(x , y) ;
        printf("%d\n" , pt) ;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值