NOIP2013货车运输

题目描述

【题意】
有n个点,编号从 1 到 n,有 m 条双向边。每一条边都有权值。现在有 q 个询问,
在x到y的所有路径中,选出一条路径,使得这条路径上面边权的最小值尽量大。
【输入描述】
第一行有两个整数 n,m。
接下来 m 行每行 3 个整数 x、y、z,表示一条权值为z的无向边。
接下来一行有一个整数 q。
接下来 q 行,每行两个整数 x、y(x 不等于 y)。
【输出描述】
输出共有 q 行,每行一个整数,如果该询问的x和y不连通,输出-1。

【样例输入】
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
【样例输出】
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。

 

给出一个非常简洁的题目

看到这样的题,如果不是知道听说用树剖来做,我还真的要想很久

因为这一题求的是最小值尽量大,所以还是很容易想到用二分或者生成树的

但是因为有q个询问,二分明显是不行的(你怎么判定这个值是否可以)

所以就只能用生成树了

首先肯定是最大生成树,因为要让边尽量大,而且也不会影响结果(既然树上可以这样走,那图上面也肯定可以这样走)

然而,这并不是一个联通图,所以我们要定义一个root,把生成树以后每一棵树连在一起来查询

记得连向根节点的边权值为正无穷

因为这道题是静态查询,所以我们只需要用lca(常数比树剖小)来查询

参考代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std ;

inline int read() {
    int x = 0 ; char s = getchar() ;
    while ( !isdigit(s) ) s = getchar() ;
    while (  isdigit(s) ) x = (x<<1) + (x<<3) + (s-48) , s = getchar() ;
    return x ;
}

const int N = 1e4 + 20 ;
const int M = 5e4 + 20 ;
const int root = 1e4 + 2 ;

struct edge {
	int v , w , nxt ;
}e[N<<1] ; int tot , last[N] ;
inline void add ( int u , int v , int w ) {
	e[++tot] = (edge){ v , w , last[u] } ;
	last[u] = tot ;
	e[++tot] = (edge){ u , w , last[v] } ;
	last[v] = tot ;
}

struct node {
	int x , y , c ;
}a[M] ; 
inline bool cmp ( node n1 , node n2 ) {
	return n1.c > n2.c ;
}

int fa[N] ;
inline int findfa ( int x ) {
	if ( fa[x] == x ) return x ;
	return fa[x] = findfa ( fa[x] ) ;
}
int n , m , q ;

int st[N][21] , b[N][21] , dep[N] ;

void dfs ( int u , int fa ) {
	st[u][0] = fa ; dep[u] = dep[fa] + 1 ;
	for ( int i = 1 ; (1<<i) < dep[u] ; i ++ ) {
		st[u][i] = st[st[u][i-1]][i-1] ;
		b[u][i] = min ( b[u][i-1] , b[st[u][i-1]][i-1] ) ;
	}
	for ( int i = last[u] ; i != -1 ; i = e[i].nxt ) {
		int v = e[i].v ; if ( v == fa ) continue ;
		b[v][0] = e[i].w ; dfs ( v , u ) ;
	}
}
int lca ( int x , int y ) {
	if ( dep[x] < dep[y] ) swap ( x , y ) ;
	int minn = 999999999 ;
	for ( int i = 20 ; i >= 0 ; i -- ) 
		if ( dep[x]-dep[y] >= (1<<i) ) {
			minn = min ( minn , b[x][i] ) ;
			x = st[x][i] ;
		}
	if ( x == y ) return minn ;
	for ( int i = 20 ; i >= 0 ; i -- ) 
		if ( (1<<i) < dep[x] && st[x][i] != st[y][i] ) {
			minn = min ( minn , b[x][i] ) ;
			minn = min ( minn , b[y][i] ) ;
			x = st[x][i] , y = st[y][i] ;
		}
	if ( st[x][0] == root ) return -1 ;
	return min ( minn , min ( b[x][0] , b[y][0] ) ) ;
}
int main() {
	n = read() , m = read() ;
	for ( int i = 1 ; i <= m ; i ++ ) 
		a[i].x = read() , a[i].y = read() , a[i].c = read() ;
	sort ( a + 1 , a + m + 1 , cmp ) ; int s = 0 ; 
	for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
	memset ( last , -1 , sizeof ( last )) , tot = -1 ;
	for ( int i = 1 ; i <= m ; i ++ ) {
		int fx = findfa ( a[i].x ) ;
		int fy = findfa ( a[i].y ) ;
		if ( fx == fy ) continue ;
		add ( a[i].x , a[i].y , a[i].c ) ; fa[fx] = fy ;
		s ++ ; if ( s == n - 1 ) break ;
	}
	for ( int i = 1 ; i <= n ; i ++ ) 
		if ( fa[i] == i ) add ( root , i , 999999999 ) ;
	dfs ( root , 0 ) ; int x , y ; q = read() ;
	while ( q -- ) {
		x = read() , y = read() ;
		printf ( "%d\n" , lca(x,y) ) ;
	}
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值