P1967 [NOIP2013 提高组] 货车运输

[NOIP2013 提高组] 货车运输

题目描述

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

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

输入格式

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

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

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

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

输出格式

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

样例 #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 % 30\% 30% 的数据, 1 ≤ n < 1000 1 \le n < 1000 1n<1000 1 ≤ m < 10 , 000 1 \le m < 10,000 1m<10,000 1 ≤ q < 1000 1\le q< 1000 1q<1000

对于 60 % 60\% 60% 的数据, 1 ≤ n < 1000 1 \le n < 1000 1n<1000 1 ≤ m < 5 × 1 0 4 1 \le m < 5\times 10^4 1m<5×104 1 ≤ q < 1000 1 \le q< 1000 1q<1000

对于 100 % 100\% 100% 的数据, 1 ≤ n < 1 0 4 1 \le n < 10^4 1n<104 1 ≤ m < 5 × 1 0 4 1 \le m < 5\times 10^4 1m<5×104,$1 \le q< 3\times 10^4 $, 0 ≤ z ≤ 1 0 5 0 \le z \le 10^5 0z105

解析:

题目询问路径上边权最小值。

按照最大生成树的顺序建立克鲁斯卡尔重构树,则路径上最小边权是重构树上lca的点权。树剖或者倍增求lca

关于克鲁斯卡尔重构树,给自己打个广告

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int head[maxn], tot;
struct edge{
	int to, nxt;
}e[maxn];

struct node{
	int x, y, w;
	node(){}
	node(int x, int y, int w): x(x), y(y), w(w){}
	bool operator < (const node &b) const{
		return w > b.w;
	}	
}edg[maxn];

void add(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;	
	head[a] = tot;
} 

int n, m, q;
int siz[maxn], p[maxn], son[maxn], dep[maxn], top[maxn];
int vis[maxn];
int fa[maxn];
int find(int x){
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void dfs1(int u, int pa){
	siz[u] = 1;
	vis[u] = 1;
	dep[u] = dep[pa] + 1;
	p[u] = pa;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == pa)
			continue;
		dfs1(v, u);
		if(siz[son[u]] < siz[v]);
			son[u] = v;
		siz[u] += siz[v];
	}
}
void dfs2(int u, int tp){
	top[u] = tp; 
	if(son[u])
		dfs2(son[u], tp);
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == son[u] || v == p[u])
			continue;
		dfs2(v, v);
	}
}
int LCA(int a, int b){
	while(top[a] != top[b]){
		if(dep[top[a]] > dep[top[b]]) 
          a = p[top[a]];
    	else
          b = p[top[b]];
	}
	return dep[a] > dep[b] ? b : a;
}

int nodetot;
int val[maxn];
void solve(){

	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		fa[i] = i;
	for(int i = 1; i <= m; i++){
		int x, y, z;
		cin >> x >> y >> z;
		edg[i] = node(x, y, z);
	}
	
	nodetot = n;
	sort(edg+1, edg+m+1);	
	for(int i = 1; i <= m; i++){
		int fx = find(edg[i].x);
		int fy = find(edg[i].y);
		if(fx == fy)
			continue;
		val[++nodetot] = edg[i].w;
		fa[nodetot] = fa[fx] = fa[fy] = nodetot;
		add(nodetot, fx); add(nodetot, fy);
		add(fx, nodetot); add(fy, nodetot);	
	}
	
	for(int i = 1; i <= nodetot; i++){
		if(!vis[i]){
			int fx = find(i);
			dfs1(fx, 0);
			dfs2(fx, fx);
		}
	}
	cin >> q;
	for(int i = 1; i <= q; i++){
		int x, y;
		cin >> x >> y;
		int fx = find(x);
		int fy = find(y);
		if(fx != fy)
			cout << -1 << endl;
		else{
			int lca = LCA(x, y);
			cout << val[lca] << endl;
		}		
	}	
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T = 1;
	while(T--)
		solve();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
noip2013普及组初赛是全国信息学奥林匹克联赛的一场选拔赛。该比赛旨在选拔初学者,对编程和算法有一定基础的学生,通过比赛形式来考察他们的知识水平和解题能力。 比赛题目通常会涉及各个领域的算法和数据结构,如图论、动态规划、数论等。题目难度逐步增加,从简单的输出结果,到复杂的程序设计与代码实现,考察选手的逻辑思维和编程能力。 参赛选手需要通过自己的思考和编程实现来解决题目,同时时间也是一个重要因素。比赛中,选手需要在规定的时间内独立完成所有题目,对于复杂的题目需要迅速想出解题思路并进行编码。因此,在比赛中,选手的临场发挥和解题速度也是需要考虑的因素。 noip2013普及组初赛的结果将作为选拔阶段的一个重要依据,选出表现出色的选手进入到更高阶段的比赛,对于他们来说,这是一次展示自己实力的机会。 此外,noip2013普及组初赛,也给了参赛选手一个交流的平台。选手们可以通过比赛结交同好,相互切磋,共同进步。同时,比赛结束后,还有详细的解题分析和讲解,有助于参赛选手对自己在比赛中的不足进行反思与改进。 总之,noip2013普及组初赛是一个考察学生编程和算法能力的选拔赛,通过比赛的形式来选拔出优秀的选手。这对于参赛选手来说,是一次展示自己才华的机会,也是一个展示自己实力和提高自己能力的平台。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值