#2018 German Collegiate Programming Contest (GCPC 18) M (MST + LCA + 倍增)

题目大意 :

有一个 N * M 的矩阵, 每个值代表该点的山有多高, Q次询问, 每次询问两点之间的路径中, 经过的最大值的最小值是多少

(可以向上下左右四个方向走)

思路 :

首先1e5次询问肯定不能直接暴力写,不难想到建立MST, 那么树上的路径即为最短的路径, 两个点的路径唯一, 在求LCA的过程中就可以求出路径当中点权的最值问题了, 通过倍增可以让最值维护优化, 但是要注意, LCA最后结束的时候还要对于x和y的点权取个最大值, 因为LCA倍增算法是不断逼近于最近共同祖先的, 无法直接到达, 所以才会有 if (p[x][i] == p[y][i]) continue 这一步,这个BUG找了4个多小时, 最后如果两个点相同, 输出该点的高度就好

Accepted code

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 2e5 + 100;
const int MAXX = 2e6 + 100;
const int MAXM = 510;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

struct Edge
{
	int v, w;
};
struct node
{
	int x, y, w;
	bool operator < (const node &oth) const
	{
		return w < oth.w;
	}
}t[MAXX];
vector <Edge> e[MAXN << 1];
vector <int> s;
int dep[MAXN], p[MAXN][25], st[MAXN][25];
int n, m, q, tot;
int h[MAXM][MAXM], fa[MAXN];
int find_(int x) {
	while (x != fa[x]) x = fa[x] = fa[fa[x]];
	return x;
}
void unite(int x, int y) {
	x = find_(x);
	y = find_(y);
	if (x != y) fa[x] = y;
}
void dfs(int x, int fa) {
	dep[x] = dep[fa] + 1, p[x][0] = fa;
	for (int i = 1; (1 << i) <= dep[x]; i++) {
		p[x][i] = p[p[x][i - 1]][i - 1];
		st[x][i] = max(st[x][i - 1], st[p[x][i - 1]][i - 1]);
	}
	for (int i = 0; i < SZ(e[x]); i++) {
		int vi = e[x][i].v;
		if (vi != fa) st[vi][0] = e[x][i].w, dfs(vi, x);
	}
}
int LCA(int x, int y) {
	int max_ = -INF;
	if (dep[x] > dep[y]) swap(x, y);
	for (int i = 20; i >= 0; i--) {
		if (dep[y] - (1 << i) >= dep[x])
			Max(max_, st[y][i]), y = p[y][i];
	}
	if (x == y) return max_;
	for (int i = 20; i >= 0; i--) {
		if (p[x][i] == p[y][i]) continue;
		Max(max_, max(st[x][i], st[y][i]));
		x = p[x][i], y = p[y][i];
	}
	return max(max_, max(st[x][0], st[y][0])); // 最后要取max
}

int main()
{
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			sc("%d", &h[i][j]);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			fa[(i - 1) * m + j] = (i - 1) * m + j;
			int w1 = h[i][j], w2 = h[i][j + 1];
			int w3 = h[i + 1][j];
			int id1 = (i - 1) * m + j, id2 = (i - 1) * m + j + 1;
			int id3 = i * m + j;
			if (i < n) t[++tot] = { id1, id3, max(w1, w3) }; // 建图
			if (j < m) t[++tot] = { id1, id2, max(w1, w2) };
		}
	}
	sort(t + 1, t + tot + 1);
	int K = 0;
	for (int i = 1; i <= tot; i++) {
		if (K == n * m - 1) break;
		int ui = t[i].x, vi = t[i].y, wi = t[i].w;
		if (find_(ui) != find_(vi)) {
			unite(ui, vi);
			e[ui].push_back({ vi, wi });
			e[vi].push_back({ ui, wi });
			K++;
		}
	}
	dfs(1, 0);
	for (int i = 0; i < q; i++) {
		int xi, yi, xx, yy;
		sc("%d %d %d %d", &xi, &yi, &xx, &yy);
		int ui = (xi - 1) * m + yi;
		int vi = (xx - 1) * m + yy;
		if (ui == vi) printf("%d\n", h[xi][yi]);
		else printf("%d\n", LCA(ui, vi));
	}
	return 0;  // 改数组大小!!!
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/103094181