这道数据结构题真的变态。
看到
M
M
才 但
N
N
有 ,想到使用线段树来维护行,又由于询问与连通块有联系,因此又想到并查集。
具体地,线段树每个节点如果表示区间
[l,r]
[
l
,
r
]
,则它要储存:
ans
a
n
s
: 第
l
l
行到第 行,包含建筑的连通块个数。
set
s
e
t
:一个并查集,储存了第
l
l
行 个格子和第
r
r
行 个格子共
2M
2
M
个格子的连通性,以及每个连通块是否与建筑物连通。
而此题的变态之处就是两个子节点的合并。
合并时,先把两个子节点的
ans
a
n
s
加起来。然后把两个儿子的四个边界放在一起,对于左儿子的右边界和右儿子的左边界,如果两个格子相邻并且可以连通,那么就把这两个格子所在的连通块合并,如果合并之前两个连通块都有建筑,那么就把
ans
a
n
s
减
1
1
。
查询时区间的合并类似。
复杂度 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
inline char get() {
char c; while ((c = getchar()) != 'O' && c != '-'
&& c != '|' && c != '+' && c != '.' && c != 'Q' && c != 'C'); return c;
}
const int N = 1e5 + 5, M = 26, L = N << 2;
int n, m, q, to[M], vis[M], tfa[M];
char s[N][M]; bool tis[M];
bool orz0(char c) {return c == 'O' || c == '-' || c == '+';}
bool orz1(char c) {return c == 'O' || c == '|' || c == '+';}
struct cyx {
int cnt, fa[M]; bool is[M];
int cx(int x) {if (fa[x] != x) fa[x] = cx(fa[x]); return fa[x];}
bool zm(int x, int y) {
int ix = cx(x), iy = cx(y);
if (ix != iy) {
bool res = is[ix] && is[iy];
fa[iy] = ix; is[ix] |= is[iy]; is[iy] = 0; return res;
}
return 0;
}
void init(int r) {
int i; cnt = 0; For (i, 1, m << 1) is[fa[i] = i] = 0;
For (i, 1, m) if (s[r][i] == 'O') is[i] = is[i + m] = 1;
For (i, 1, m) zm(i, i + m); For (i, 1, m - 1)
if (orz0(s[r][i]) && orz0(s[r][i + 1])) zm(i, i + 1);
For (i, 1, m << 1) if (fa[i] == i && is[i]) cnt++;
}
} T[L];
cyx mer(cyx a, cyx b, int mid) {
int i, x; cyx res; res.cnt = a.cnt + b.cnt;
For (i, 1, m) {
res.fa[i] = a.fa[i] <= m ? a.fa[i] : a.fa[i] + m;
res.fa[i + m] = b.fa[i + m] > m ? b.fa[i + m] : b.fa[i + m] + m * 3;
res.fa[i + (m << 1)] = a.fa[i + m] > m ? a.fa[i + m] + m : a.fa[i + m];
res.fa[i + m * 3] = b.fa[i] <= m ? b.fa[i] + m * 3 : b.fa[i];
res.is[i] = a.is[i]; res.is[i + m] = b.is[i + m];
res.is[i + (m << 1)] = a.is[i + m]; res.is[i + m * 3] = b.is[i];
}
For (i, 1, m) if (orz1(s[mid][i]) && orz1(s[mid + 1][i]) &&
res.zm(i + (m << 1), i + m * 3)) res.cnt--;
For (i, 1, m << 2) vis[i] = 0, to[i] = 0;
For (i, 1, m << 1) x = res.cx(i), vis[x] = res.is[x] ? 1 : -1, to[x] = i;
For (i, 1, m << 1) tfa[i] = to[res.cx(i)], tis[i] = 0;
For (i, 1, m << 2) if (vis[i]) tis[to[i]] = vis[i] == 1;
For (i, 1, m << 1) res.fa[i] = tfa[i], res.is[i] = tis[i]; return res;
}
void build(int l, int r, int p) {
if (l == r) return T[p].init(l);
int mid = l + r >> 1; build(l, mid, p2); build(mid + 1, r, p3);
T[p] = mer(T[p2], T[p3], mid);
}
void update(int l, int r, int pos, int p) {
if (l == r) return T[p].init(pos);
int mid = l + r >> 1; if (pos <= mid) update(l, mid, pos, p2);
else update(mid + 1, r, pos, p3); T[p] = mer(T[p2], T[p3], mid);
}
cyx query(int l, int r, int s, int e, int p) {
if (l == s && r == e) return T[p]; int mid = l + r >> 1;
if (e <= mid) return query(l, mid, s, e, p2);
else if (s >= mid + 1) return query(mid + 1, r, s, e, p3);
else return mer(query(l, mid, s, mid, p2),
query(mid + 1, r, mid + 1, e, p3), mid);
}
int main() {
int i, j, x, y; char c; n = read(); m = read();
For (i, 1, n) For (j, 1, m) s[i][j] = get(); build(1, n, 1);
q = read(); while (q--) {
c = get(); x = read(); y = read();
if (c == 'C') s[x][y] = get(), update(1, n, x, 1);
else printf("%d\n", query(1, n, x, y, 1).cnt);
}
return 0;
}