1. 程式人生 > >[BZOJ3204][Sdoi2013]城市規劃(線段樹+並查集)

[BZOJ3204][Sdoi2013]城市規劃(線段樹+並查集)

這道資料結構題真的變態。
看到 M6N105 ,想到使用線段樹來維護行,又由於詢問與連通塊有聯絡,因此又想到並查集。
具體地,線段樹每個節點如果表示區間 [l,r] ,則它要儲存:
ans : 第 l 行到第 r 行,包含建築的連通塊個數。
set :一個並查集,儲存了第 lM 個格子和第 rM 個格子共 2M 個格子的連通性,以及每個連通塊是否與建築物連通。
而此題的變態之處就是兩個子節點的合併。
合併時,先把兩個子節點的 ans 加起來。然後把兩個兒子的四個邊界放在一起,對於左兒子的右邊界和右兒子的左邊界,如果兩個格子相鄰並且可以連通,那麼就把這兩個格子所在的連通塊合併,如果合併之前兩個連通塊都有建築,那麼就把

ans1
查詢時區間的合併類似。
複雜度 O(NMlogN)
程式碼:

#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; }