洛谷1967货物运输(kruskal+lca倍增)

4 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

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求最大生成树,发现加入一条边使x,y联通,则该条边的权值为所求。易证。

有一个巧妙的想法,因为边是由大到小加进来的,所以越往上的祖先所管的边权越小,因此我们只要跑一下最大生成树再跑lca便可求解。


【代码】

<span style="font-size:18px;">#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=100005;
struct data{
    int u,v,w;
    bool operator <(const data&b)const{
	    return(w>b.w);
	}
}e[maxn];
int q,n,m,anc[maxn],fa[maxn],cnt=1,num,dep[maxn],f[maxn][19],val[maxn];
inline int get(){
    char c;while(!isdigit(c=getchar()));
    int p=c-48;while(isdigit(c=getchar()))p=p*10+c-48;
    return p;
}
inline int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    int j=0;
    while((1<<(j+1))<=dep[a])++j;
    for(int i=j;i>=0;--i){               //注意i可以取零
	    if((dep[a]-(1<<i))>=dep[b])a=f[a][i];
	}
	if(a==b)return a;
	for(int i=j;i>=0;--i){
	    if(f[a][i] && f[a][i]!=f[b][i]){
		    a=f[a][i];
		    b=f[b][i];
		}
	}
	return fa[a];
}
inline int Find(int x){
    if(x==anc[x])return anc[x];
	else return (anc[x]=Find(anc[x])); 
}
int main(){
	memset(f,0,sizeof(f));
	memset(fa,0,sizeof(fa));
    n=get();m=get();
    for(int i=1;i<=m;++i)e[i].u=get(),e[i].v=get(),e[i].w=get();
    sort(e+1,e+1+m);num=n;
    for(int i=1;i<=n;++i)anc[i]=i;
    for(int i=1;i<=m;++i){
	    int x=Find(anc[e[i].u]);
	    int y=Find(anc[e[i].v]);
	    if(x!=y){
		    anc[x]=anc[y]=fa[x]=fa[y]=++n; //想象一下
		    fa[n]=0,val[n]=e[i].w,anc[n]=n;
		    ++cnt;if(cnt==num)break;
		}
	}
	dep[n]=0;
	for(int i=n-1;i;--i){
	    dep[i]=dep[fa[i]]+1;
	    f[i][0]=fa[i];
	    int j=1;
	    while(f[f[i][j-1]][j-1])f[i][j]=f[f[i][j-1]][j-1],++j;
	}
	q=get();
	while(q--){
	    int x=get(),y=get();
	    if(Find(x)!=Find(y))printf("-1\n");
	    else printf("%d\n",val[lca(x,y)]);
	}
	return 0;
}</span>



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值