CodeForces 1920F2 Smooth Sailing (Hard Version)

洛谷传送门

CF 传送门

首先需要知道的一个 trick:判断一个点是否在一个闭合回路内部,从这个点向任意方向引一条射线,若不考虑相切,那么和回路的交点为奇数时这个点在回路内部,否则在外部。

那么这题要判断一个回路是否包含全部的 island,可以找到任意一个 island 向右引一条射线。

给每个点增加一个状态 \((x, y, 0/1)\) 表示当前走到 \((x, y)\),穿过了偶数或奇数次射线。那么一次询问的本质是找到一条 \((x, y, 0) \to (x, y, 1)\) 的一条经过点权最小值最大的路径(可以多源 bfs 求出任意一点 \((i, j)\) 到最近的 v 的距离 \(d_{i, j}\)\((x, y, 0/1)\) 的点权就是 \(d_{x, y}\))。

上面那个问题显然给每条 \((u, v)\) 赋权 \(\min(val_u, val_v)\),就可以建 Kruskal 重构树查 LCA 解决。

建图就对于一个 \((x, y)\),如果它在射线上就视为 \((x, y) \to (x + 1, y)\) 和射线新增了一个交点。

时间复杂度 \(O((nm + q) \log nm)\)

code
// Problem: F2. Smooth Sailing (Hard Version)
// Contest: Codeforces - Codeforces Round 919 (Div. 2)
// URL: https://codeforces.com/contest/1920/problem/F2
// Memory Limit: 1024 MB
// Time Limit: 5000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define ID(x, y) (((x) - 1) * m + (y))
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 1200100;
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

int n, m, q, f[maxn], tot, ff[maxn];
string s[maxn];

struct edge {
	int u, v, d;
	edge(int a = 0, int b = 0, int c = 0) : u(a), v(b), d(c) {}
} E[maxn * 5];

vector<int> G[maxn];
int fa[maxn], sz[maxn], son[maxn], dep[maxn], top[maxn], a[maxn];

int dfs(int u, int f, int d) {
	fa[u] = f;
	sz[u] = 1;
	dep[u] = d;
	int mx = -1;
	for (int v : G[u]) {
		if (v == f) {
			continue;
		}
		sz[u] += dfs(v, u, d + 1);
		if (sz[v] > mx) {
			son[u] = v;
			mx = sz[v];
		}
	}
	return sz[u];
}

void dfs2(int u, int tp) {
	top[u] = tp;
	if (!son[u]) {
		return;
	}
	dfs2(son[u], tp);
	for (int v : G[u]) {
		if (!top[v]) {
			dfs2(v, v);
		}
	}
}

inline int qlca(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	return x;
}

int find(int x) {
	return ff[x] == x ? x : ff[x] = find(ff[x]);
}

void solve() {
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n * m * 4; ++i) {
		ff[i] = i;
	}
	int X = -1, Y = -1;
	for (int i = 1; i <= n; ++i) {
		cin >> s[i];
		s[i] = " " + s[i] + " ";
		for (int j = 1; j <= m; ++j) {
			if (s[i][j] == '#') {
				X = i;
				Y = j;
			}
		}
	}
	queue<pii> Q;
	mems(f, -1);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (s[i][j] == 'v') {
				f[ID(i, j)] = 0;
				Q.emplace(i, j);
			}
		}
	}
	while (Q.size()) {
		int x = Q.front().fst, y = Q.front().scd;
		Q.pop();
		for (int i = 0; i < 4; ++i) {
			int nx = x + dx[i], ny = y + dy[i];
			if (nx < 1 || nx > n || ny < 1 || ny > m) {
				continue;
			}
			if (f[ID(nx, ny)] == -1) {
				f[ID(nx, ny)] = f[ID(x, y)] + 1;
				Q.emplace(nx, ny);
			}
		}
	}
	int tot = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (s[i][j] == '#') {
				continue;
			}
			if (i < n && s[i + 1][j] != '#') {
				int d = min(f[ID(i, j)], f[ID(i + 1, j)]);
				if (i == X && j > Y) {
					E[++tot] = edge(ID(i, j), ID(i + 1, j) + n * m, d);
					E[++tot] = edge(ID(i, j) + n * m, ID(i + 1, j), d);
				} else {
					E[++tot] = edge(ID(i, j), ID(i + 1, j), d);
					E[++tot] = edge(ID(i, j) + n * m, ID(i + 1, j) + n * m, d);
				}
			}
			if (j < m && s[i][j + 1] != '#') {
				int d = min(f[ID(i, j)], f[ID(i, j + 1)]);
				E[++tot] = edge(ID(i, j), ID(i, j + 1), d);
				E[++tot] = edge(ID(i, j) + n * m, ID(i, j + 1) + n * m, d);
			}
		}
	}
	sort(E + 1, E + tot + 1, [&](const edge &a, const edge &b) {
		return a.d > b.d;
	});
	int nt = n * m * 2;
	for (int i = 1; i <= tot; ++i) {
		int x = find(E[i].u), y = find(E[i].v), d = E[i].d;
		if (x != y) {
			int z = ++nt;
			ff[x] = ff[y] = z;
			a[z] = d;
			G[z].pb(x);
			G[z].pb(y);
		}
	}
	dfs(nt, -1, 1);
	dfs2(nt, nt);
	while (q--) {
		int x, y;
		scanf("%d%d", &x, &y);
		printf("%d\n", a[qlca(ID(x, y), ID(x, y) + n * m)]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-01-14 15:08  zltzlt  阅读(46)  评论(0编辑  收藏  举报