UVa 11354 Bond 最小生成树+LCA倍增

题目大意: n 个点 m 条边的图 q 次询问,每条边都有一个危险系数,每次询问要求一条从 s 到 t 的路径使所有边的最大危险系数最小

思路:

先求出最小生成树,这样能保证图中所有路径的值最小。

接着dfs建有根树,记录 fa[i] 和 val[i] 分别表示节点 i 的父亲节点编号和 i 与父亲的边的权值,同时处理出深度deep[i]

利用倍增思想预处理出LCA和两点之间路径的最大权值记为 STfa[i][j] , STcost[i][j]

接着就是每次 logn 的询问了


细节看代码:

/*************************************************************************
	 > Author: wzw-cnyali
	 > Created Time: 2017/9/11 12:41:53
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>

#prag\
ma GCC optimize("O3")

using namespace std;

#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
#define mem(a, b) memset((a), b, sizeof(a))

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

template <class T>
T read(T sum = 0, T fg = 0)
{
	char c = getchar();
	while(c < '0' || c > '9') { fg |= c == '-'; c = getchar(); }
	while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); }
	return fg ? -sum : sum;
}

const int Size = 100010;

struct Edge
{
	int x, y, w;
	void input()
	{
		x = read<int>();
		y = read<int>();
		w = read<int>();
	}
	friend bool operator < (Edge a, Edge b)
	{
		return a.w < b.w;
	}
}edge[Size];

int to[Size], nxt[Size], be[Size], w[Size], e;

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

void add_edge(int x, int y, int z) { add(x, y, z); add(y, x, z); }

void init() { mem(be, -1); e = 0; }

int n, m;

int fa[Size];

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

bool merge(int x, int y)
{
	int fx = father(x), fy = father(y);
	if(fx != fy) { fa[fx] = fy; return 1; }
	else return 0;
}

void Kruskal()
{
	init();
	REP(i, 1, n) fa[i] = i;
	sort(edge + 1, edge + m + 1);
	int count = 0;
	REP(i, 1, m)
	{
		int x = edge[i].x, y = edge[i].y, w = edge[i].w;
		if(merge(x, y))
		{
			add_edge(x, y, w);
			if(++count == n - 1) break;
		}
	}
}

int deep[Size], Fa[Size], val[Size];

void dfs_init(int x, int f, int depth)
{
	Fa[x] = f; deep[x] = depth;
	EREP(i, x)
	{
		int y = to[i];
		if(y != f)
		{
			val[y] = w[i];
			dfs_init(y, x, depth + 1);
		}
	}
}

int STfa[Size][20];
int STcost[Size][20];

void ST_init()
{
	mem(STfa, -1);
	REP(i, 1, n)
	{
		STcost[i][0] = val[i];
		STfa[i][0] = Fa[i];
	}
	for(int j = 1; (1 << j) <= n; ++j)
	{
		REP(i, 1, n)
		{
			int a = STfa[i][j - 1];
			if(a != -1)
			{
				STfa[i][j] = STfa[a][j - 1];
				STcost[i][j] = max(STcost[i][j - 1], STcost[a][j - 1]);
			}
		}
	}
}

int query(int L, int R)
{
	if(deep[L] < deep[R]) swap(L, R);
	int lg = log2(deep[L]);
	int ans = -1;
	DREP(i, lg, 0)
	{
		if(deep[L] - (1 << i) >= deep[R])
		{
			chkmax(ans, STcost[L][i]);
			L = STfa[L][i];
		}
	}
	if(L == R) return ans;
	DREP(i, lg, 0)
	{
		if(STfa[L][i] != -1 && STfa[L][i] != STfa[R][i])
		{
			chkmax(ans, STcost[L][i]);
			chkmax(ans, STcost[R][i]);
			L = STfa[L][i]; R = STfa[R][i];
		}
	}
	chkmax(ans, val[L]); chkmax(ans, val[R]);
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.in", "r", stdin);
	freopen("output.out", "w", stdout);
#endif
	int Case;
	while(scanf("%d%d", &n, &m) != EOF)
	{
		if(Case++) puts("");
		REP(i, 1, m) edge[i].input();
		Kruskal();
		dfs_init(1, -1, 0);
		ST_init();
		int q = read<int>();
		while(q--)
		{
			int x = read<int>(), y = read<int>();
			int ans = query(x, y);
			printf("%d\n", ans);
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值