[NOIp2013] 货车运输

题目大意:给一个n(0 < n < 10000)个点,m(0 < m < 50000)条边的带权双向图,给q(0 < q < 30000)个询问,每次询问两个点x, y,输出x到y所有路径上最小边的最大值。

分析:

因为要求所有路径上最小边的最大值,所以先求一遍最大生成树,通过最大生成树的边求得的路径一定是最大的,求的过程中用邻接表建图,之后对于每个询问x, y,用st-lca求x, y的最近公共祖先(LCA),求的过程中对经过的路程取min.

那么,这里介绍一下st-lca算法:

用dep[i]表示结点i的深度,f[i][j]表示结点i的2^j祖先,minv[i][j]表示结点i到其2^j祖先路径上的最小值。

第一步,dfs,得到所有点的dep值,初始化f和minv.

第二步,得到所有的f和minv,其中f[i][j] = f[f[i][j-1]][j-1],minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]).

第三步,开始正式lca过程:

1.将y调整为深度比较大的。

2.将询问的两个点调整至相同深度。

3.将两个点调整至lca的儿子。

4.最后返回答案的时候,如果lca是0,那么表示无解,否则最后答案要注意ans = min(ans, min(minv[x][0], minv[y][0])).

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, m, q, x, y, cnt, hd[10005], nxt[20005], to[20005], w[20005], p[50005], f[10005][20], minv[10005][20], dep[10005];
struct edge {
	int x, y, z;
	bool operator < (const edge &rhs) const {
		return z > rhs.z;
	}
}e[50005];

void add(int x, int y, int z) {
	to[cnt] = y;
	w[cnt] = z;
	nxt[cnt] = hd[x];
	hd[x] = cnt++;
}

int fnd(int x) {
	return p[x] == x ? x : p[x] = fnd(p[x]);
}
void kruskal() {
	for(int i = 0; i < m; i++) p[i] = i;
	sort(e, e+m);
	for(int i = 0; i < m; i++) if(fnd(e[i].x) != fnd(e[i].y)) {
		add(e[i].x, e[i].y, e[i].z);
		add(e[i].y, e[i].x, e[i].z);
		p[fnd(e[i].x)] = fnd(e[i].y);
	}
}

void dfs(int x, int p) {
	for(int i = hd[x]; ~i; i = nxt[i]) if(i != p) {
		dep[to[i]] = dep[x] + 1;
		f[to[i]][0] = x;
		minv[to[i]][0] = w[i];
		dfs(to[i], i ^ 1);
	}
}

int lca(int x, int y) {
	int ans = 100000000;
	if(dep[x] > dep[y]) swap(x, y);
	for(int i = 15; i >= 0; i--) if(dep[f[y][i]] >= dep[x]) {
		ans = min(ans, minv[y][i]);
		y = f[y][i];
	}
	if(x == y) return ans;
	for(int i = 15; i >= 0; i--) if(f[x][i] != f[y][i]) {
		ans = min(ans, min(minv[x][i], minv[y][i]));
		x = f[x][i];
		y = f[y][i];
	}
	return f[x][0] == 0 ? -1 : min(ans, min(minv[x][0], minv[y][0]));
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
	memset(hd, -1, sizeof hd);
	kruskal();
	dep[1] = 1;
	dfs(1, -1);
	for(int j = 1; j <= 15; j++)
	for(int i = 1; i <= n; i++) {
		f[i][j] = f[f[i][j-1]][j-1];
		minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]);
	}
	scanf("%d", &q);
	while(q--) scanf("%d%d", &x, &y), printf("%d\n", lca(x, y));
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值