CodeForces 1920F2 Smooth Sailing (Hard Version)
首先需要知道的一个 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;
}