NKOI 2495 火车运输

28 篇文章 0 订阅

【NOIP2013-D1T3】货车运输

Time Limit:10000MS  Memory Limit:128000K
Total Submit:95 Accepted:57
Case Time Limit:1000MS

Description

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

Input

第一行有两个用一个空格隔开的整数 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

输出共有 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%的数据,1<=n<=1000,1<=m<=10000,1<=q<=1000;
对于 60%的数据,1<=n<=1000,1<=m<=50000,1<=q<=1000;
对于 100%的数据,1<=n<=10000,1<=m<=50000,1<=q<=30000,0<=z<=100000。


这道题也是一道LCA类的题,但是并没有那么简单了

题意大致为:给你一个图,求出其中任意两点所有路径中最短边的最大值,如果两点无法到达,输出-1

那么这道题我们就可以先用最大生成森林求出最长的路径,由于求最大生成树要将所有边从大到小排序,那么既保证了最小值最大,又避免了对重边的讨论,求最大生成森林的时候要注意并查集的写法

然后我们预处理f(i,j)表示第i个点距离为2^j的祖先,这个可以深搜整棵树再递推一下,复杂度nlogn

minx(i,j)表示第i个点到其距离为2^j的祖先上的最小权值,然后用倍增的思想俩个点往上跳一跳更新答案就可以了

然而看起来很容易,实际上代码还有许多细节需要注意,一个不小心就得找几个小时的错,一定要万分注意

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

const int inf=1e9;
const int maxn=10005;
int n,quest,a,b,depth[maxn],f[maxn][20],m,dis[maxn],s;
int END[maxn<<1],len[maxn<<1],NEXT[maxn<<1],last[maxn];
int father[maxn],tot,minx[maxn][20];
bool mark[maxn];
struct wk{int a,b,c;}edge[maxn*5];
bool cmp(wk a,wk b){return a.c>b.c;}

inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}

void insert(int a,int b,int l){
	END[++tot]=b;
	len[tot]=l;
	NEXT[tot]=last[a];
	last[a]=tot;
}

int finder(int x){return father[x]==x?x:father[x]=finder(father[x]);}

void dfs(int v,int dpt){//fs建树顺便计算f,minx数组
	mark[v]=1;depth[v]=dpt;
	int i,j,k=ceil(log(depth[v])/log(2));
	for(i=1;i<=k;i++){
		f[v][i]=f[f[v][i-1]][i-1];
	    minx[v][i]=min(minx[v][i-1],minx[f[v][i-1]][i-1]);
	}
	for(i=last[v];i;i=NEXT[i])
	    if(!mark[END[i]]){
	    	f[END[i]][0]=v;
	    	minx[END[i]][0]=len[i];
	    	dfs(END[i],dpt+1);
	    }
}

int LCA(int x,int y){
	int i,k,ans=inf;
	if(depth[x]<depth[y])swap(x,y);
	k=depth[x]-depth[y];
	for(i=0;i<=s;i++)
	    if(k&(1<<i)){
	    	ans=min(ans,minx[x][i]);//这里的两个赋值语句绝对不能换位置,因为计算的时候是计算的跳到祖先之前的x点
	    	x=f[x][i];
		}
	if(x==y)return ans;
	k=ceil(log(depth[x])/log(2));
	for(i=k;i>=0;i--)
	    if(f[x][i]!=f[y][i]){
	    	ans=min(ans,min(minx[x][i],minx[y][i]));
		    x=f[x][i],y=f[y][i];
		}
	if(f[x][0]==n+1)return inf;
	return min(ans,min(minx[x][0],minx[y][0]));
}

void kruscal(){//最大生成森林
	int fx,fy,i;
	for(i=1;i<=m;i++){
		fx=finder(edge[i].a);
		fy=finder(edge[i].b);
		if(fx!=fy){
			father[fx]=fy;
			insert(edge[i].a,edge[i].b,edge[i].c);
			insert(edge[i].b,edge[i].a,edge[i].c);
		}//计算了一组之后要将其存边以便之后的计算
	}
	for(i=1;i<=n;i++)
	    if(finder(i)==i)insert(n+1,i,inf);//将N+1当做虚拟节点
}

int main(){
	_read(n);_read(m);
	int i,j,k;
	for(i=1;i<=m;i++){
		_read(edge[i].a);
		_read(edge[i].b);
		_read(edge[i].c);
	}
	for(i=1;i<=n;i++)father[i]=i;
	sort(edge+1,edge+1+m,cmp);
	<span style="font-family:Times New Roman;">kruscal();</span>
	s=ceil(log(n)/log(2));	  
	for(i=1;i<=n;i++)
	    for(j=0;j<=s;j++)
	        minx[i][j]=inf;//初值不能忘
	f[n+1][0]=n+1,minx[n+1][0]=inf;
	dfs(n+1,1);
	_read(quest);
	for(i=1;i<=quest;i++){
		_read(a);_read(b);
		int ans=LCA(a,b);
		printf("%d\n",ans==inf?-1:ans);
	}
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值