luogu P1967 [NOIP2013 提高组] 货车运输

原题链接:

        [NOIP2013 提高组] 货车运输 - 洛谷https://www.luogu.com.cn/problem/P1967

题目描述

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。

接下来 m 行每行三个整数x,y,z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。
注意:x!=y,两座城市之间可能有多条道路 。

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

接下来 q 行,每行两个整数 x,y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,保证x!=y

输出格式

共有 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

说明/提示

对于 100% 的数据,1≤n<10^4,1≤m<5×10^4,1≤q<3×10^4,0≤z≤105。

Solution:

        对于本题,可以发现边权小的路段永远不会走过,因为需要载重最大值,可以看出使用最大生成树

        这里使用kruskal,这里大家很容易想到一种暴力解法,将边按权值从大到小排序就是对于最大生成树的每一次合并,都对其所有的起点和终点都进行一次判断联通性,已经联通了的打上标记,对未联通的进行一次判断联通性,如果在该边在本次连边中联通了,那么该边权就是答案

        时间复杂度(nq)只能拿60分。

60 opts code:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int u,v,w;
}e[100005];
int n,m,q;
int st[30005],ed[30005];
int vis[30005];
int ans[30005];
int fa[100005];
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==fa[x]?x:fa[x] = find(fa[x]);}
void uni(int x,int y){fa[find(x)] = find(y);}
bool flag = true;
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i = 1;i<=n;++i)	fa[i] = i;
	for(int i = 1;i<=m;++i)
		cin>>e[i].u>>e[i].v>>e[i].w;
	sort(e+1,e+1+m,cmp);
	cin>>q;
	memset(ans,-1,sizeof(ans));
	for(int i = 1;i<=q;++i)
		cin>>st[i]>>ed[i];
	for(int i = 1;i<=m;++i)
	{
		if(n==1)	break;
		if(find(e[i].u)!=find(e[i].v))
		{
			uni(e[i].u,e[i].v);
			n--;
			bool flag = true;
			for(int j = 1;j<=q;++j)
			{
				if(vis[j])	continue;
				if(find(st[j])==find(ed[j]))
					ans[j] = e[i].w,vis[j] = 1;
				flag = false;	
			}
			if(flag)	break;
		}
	}
	for(int i = 1;i<=q;++i)
		cout<<ans[i]<<endl;
	return 0;
}

        所以必须想到优化,那么就只能先将整个树连完了再说,做完了最大生成树,我们可以发现一个性质,树的a点到树的b点有且只有一条路径,那是什么呢?就是a点和b点lca过程走过的路径,那么我们可以在求lca的过程中统计最小边权,最后返回

        可以在lca预处理的过程中增加一个w数组,w[x][i]为x与其第2^i个祖先的最小边权,状态转移与fa的转移类似:w[x][i] = min(w[x][i-1],w[fa[x][i-1]][i-1])

        那么这道问题,就愉快的解决啦~

ACcode:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int u,v,w;
}e[100005];
struct ee{
	int v,w;
	ee(){}
	ee(int v1,int w1):v(v1),w(w1){}
};
vector<ee> edge[100005];
int n,m,q;
int st[30005],ed[30005];
int vis[30005];
int ans[30005];
int fa[100005];
int lg[100005];
int deep[100005];
int fath[100005][25];
int w[100005][25];
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==fa[x]?x:fa[x] = find(fa[x]);}
void uni(int x,int y){fa[find(x)] = find(y);}
void dfs(int x,int ff,int num)
{
	deep[x] = deep[ff]+1;
	fath[x][0] = ff;
	w[x][0] = num;
	for(int i = 1;i<=lg[deep[x]-1];++i)
	{
		fath[x][i] = fath[fath[x][i-1]][i-1];
		w[x][i] = min(w[x][i-1],w[fath[x][i-1]][i-1]);
	}
	for(int i = 0;i<edge[x].size();++i)
		if(edge[x][i].v!=ff)	dfs(edge[x][i].v,x,edge[x][i].w);
}
int lca(int x,int y)
{
	if(find(x)!=find(y))	return -1;
	if(deep[x]<deep[y])
		swap(x,y);
	int ans = 0x3f3f3f3f;
	while(deep[x]>deep[y])
	{
		ans = min(ans,w[x][lg[deep[x]-deep[y]]-1]);
		x = fath[x][lg[deep[x]-deep[y]]-1];
	}
	if(x==y)	return ans;
	for(int k = lg[deep[x]-1];k>=0;k--)
		if(fath[x][k]!=fath[y][k])
		{
			ans = min(ans,w[x][k]);
			ans = min(ans,w[y][k]);
			x = fath[x][k];
			y = fath[y][k];
		}
	ans = min(ans,w[x][0]);
	ans = min(ans,w[y][0]);
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i = 1;i<=n;++i)	fa[i] = i;
	for(int i = 1;i<=m;++i)
		cin>>e[i].u>>e[i].v>>e[i].w;
	sort(e+1,e+1+m,cmp);
	cin>>q;
	int cnt = 0;
	for(int i = 1;i<=m;++i)
	{
		if(cnt==n-1)	break;
		if(find(e[i].u)!=find(e[i].v))
		{
			uni(e[i].u,e[i].v);
			edge[e[i].u].push_back(ee(e[i].v,e[i].w));
			edge[e[i].v].push_back(ee(e[i].u,e[i].w));
			cnt++;
		}
	}
	for(int i = 1;i<=n;++i)
		lg[i] = lg[i-1]+(1<<lg[i-1]==i);
	dfs(1,0,-1);
	for(int i = 1;i<=q;++i)
	{
		int x,y;
		cin>>x>>y;
		cout<<lca(x,y)<<endl;
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值