P1967 货车运输

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

输入格式
第一行有两个用一个空格隔开的整数 n,mn,m,表示 AA 国有 nn 座城市和 mm 条道路。

接下来 mm行每行 3 3个整数 x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从 x x号城市到 y y号城市有一条限重为 zz 的道路。注意: ** xx 不等于 yy,两座城市之间可能有多条道路 ** 。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

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

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

输入输出样例
输入
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出
3
-1
3
说明/提示
对于 30%30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,0000<n<1,000,0<m<10,000,0<q<1,000;

对于 60%60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,0000<n<1,000,0<m<50,000,0<q<1,000;

对于 100%100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,0000<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000。

算法 最大生成树+树链剖分(边权)

最大生成树的算法其实就是最小生成树的算法
用最大生成树的原因:
货车要从一个地点到另一个点要经过的道路应尽可能大,所以那些多余的路径就可以删去,
而且树上任意两点可以相互到达。
接下来问题就变成了 找两点最短路径之间边权的最小值
惊奇地发现可以用树链剖分来维护两点间最短路径的最短边权。
代码

#include<bits/stdc++.h>
using namespace std;
const int ll=5e5+5,maxn=1e5+5;
int n,m,s;
int uer[ll],ver[ll],wer[ll],f[ll],tot;
int head[ll],next[ll];
void add(int u,int v,int w){
	tot++;
	ver[tot]=v;
	wer[tot]=w;
	next[tot]=head[u];
	head[u]=tot;
}
struct lcj{
	int a,b,c;
}bian[ll];
bool cmp(lcj x,lcj y){
	return x.c>y.c;
}
int get(int x){
	if(f[x]==x) return x; 
	f[x]=get(f[x]);
	return f[x];
}
void hb(int x,int y){
	int t1=get(x),t2=get(y);
	if(t1!=t2){
		f[t1]=t2;
	}
}
int top[maxn],d[maxn],son[maxn],size[maxn],fa[maxn],dfn[maxn],pre[maxn],val[maxn],cnt,book[maxn];
void dfs1(int u,int f){
	book[u]=1;
	d[u]=d[f]+1;
	fa[u]=f;
	size[u]=1;
	for(int i=head[u];i;i=next[i]){
		int v=ver[i],w=wer[i];
		if(v==f) continue;
		dfs1(v,u);
		size[u]+=size[v];
		val[v]=w;
		if(son[u]==-1||size[son[u]]<size[v])
			son[u]=v;
	}
}
void dfs2(int u,int tp){
	book[u]=1;
	dfn[u]=++cnt;
	pre[cnt]=u;
	top[u]=tp;
	if(son[u]==-1) return ;
	if(son[u])
		dfs2(son[u],tp);
	for(int i=head[u];i;i=next[i]){
		int v=ver[i];
		if(v==son[u]||v==fa[u]) continue;
		dfs2(v,v);
	}
}
struct node{
	int l,r,data;
}t[maxn<<2];
void build(int p,int l,int r){
	t[p].l=l;
	t[p].r=r;
	if(l==r){
		t[p].data=val[pre[l]];
		return ;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build((p<<1)|1,mid+1,r);
	t[p].data=min(t[p<<1].data,t[(p<<1)|1].data);
}
int getmin(int p,int l,int r){
	if(l<=t[p].l&&r>=t[p].r){
		return t[p].data;
	}
	int mid=t[p].l+t[p].r>>1,ans=99999999;	
	if(l<=mid) ans=min(ans,getmin(p<<1,l,r));
	if(r>mid)  ans=min(ans,getmin((p<<1)|1,l,r));
	return ans;
}
int ask(int x,int y){
	int ans=99999999;
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]]) swap(x,y);
		ans=min(ans,getmin(1,dfn[top[x]],dfn[x]));		
		x=fa[top[x]];
	}
	if(x==y) return ans;	
	if(d[x]>d[y]) swap(x,y);
	ans=min(ans,getmin(1,dfn[x]+1,dfn[y]));
	return ans;
}
int main(){
	memset(son,-1,sizeof(son));
	memset(val,0x3f3f,sizeof(val));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)	
		scanf("%d%d%d",&bian[i].a,&bian[i].b,&bian[i].c);	
	sort(bian+1,bian+m+1,cmp);		
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++){
		int u=bian[i].a,v=bian[i].b,w=bian[i].c;
		int t1=get(u),t2=get(v);
		if(t1==t2) continue;
		hb(t1,t2);
		add(u,v,w);
		add(v,u,w);
	}
	for(int i=1;i<=n;i++)
		if(!book[i])
		dfs1(i,0);
	memset(book,0,sizeof(book));
	for(int i=1;i<=n;i++)
		if(!book[i])
		dfs2(i,i);
	build(1,1,n);
	
	scanf("%d",&s);
	while(s--){
		int x,y;
		scanf("%d%d",&x,&y);
		if(get(x)!=get(y)){
			printf("-1\n");
			continue;			
		}
		printf("%d\n",ask(x,y));
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值