#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;  // 改數組大小!!!
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章