[BZOJ3204][Sdoi2013]城市規劃(線段樹+並查集)
阿新 • • 發佈:2019-02-13
這道資料結構題真的變態。
看到 才 但 有 ,想到使用線段樹來維護行,又由於詢問與連通塊有聯絡,因此又想到並查集。
具體地,線段樹每個節點如果表示區間 ,則它要儲存:
: 第 行到第 行,包含建築的連通塊個數。
:一個並查集,儲存了第 行 個格子和第 行 個格子共 個格子的連通性,以及每個連通塊是否與建築物連通。
而此題的變態之處就是兩個子節點的合併。
合併時,先把兩個子節點的 加起來。然後把兩個兒子的四個邊界放在一起,對於左兒子的右邊界和右兒子的左邊界,如果兩個格子相鄰並且可以連通,那麼就把這兩個格子所在的連通塊合併,如果合併之前兩個連通塊都有建築,那麼就把 減 。
查詢時區間的合併類似。
複雜度 。
程式碼:
#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;
}