NOIP2021 棋局
推销:Everyday DS | Day \(S_{\text{fib}}(7)\)。
历时 5.5h 写 + 调,真的有人会在场上写正解吗。/oh/oh
考虑以某些同种种类的边组成的若干连通块,注意到放一个棋子可能会将棋盘分割成不同的连通块,于是倒序考虑将分裂变成合并,每次相当于删去一个棋子。
\((x,y)\) 的答案分四类讨论,分别为 \(S_1,S_2,S_3,S_4\):
- \(S_1\) 为 \((x,y)\) 能通过 \(2\) 类边到达的所有点个数。
- \(S_2\) 为 \((x,y)\) 能通过 \(3\) 类边到达的所有点个数。
- \(S_3\) 为 \((x,y)\) 分别通过 \(2\) 类边和 \(3\) 类边都能到达的所有点个数。
- \(S_4\) 为 \((x,y)\) 通过 \(1\) 类边能到达的,通过 \(2,3\) 类边均不能到达的点的个数。
答案就是 \(S_1+S_2-S_3+S_4\)。考虑求出以上答案所需要维护的信息,注意到我们要支持合并的数据结构,定义 \((x,y)\) 的横向坐标为 \((x-1)m+y\),纵向坐标为 \((y-1)n+x\),则一行的子段横向坐标连续、一列的子段纵向坐标连续:
- \(S_1\):对每行/每列建并查集维护其中 \(2\) 类边组成的连通块,由于只能走水平/竖直方向,每个连通块在横向/纵向坐标中是区间的形式,记录每个区间左端点以及右端点即可。
- \(S_2,S_3\)(因为有些限制重复所以放一起了):对每个点建 \(4\) 棵线段树,维护这个点所在的 \(3\) 类边连通块(这里 \(3\) 类边 \((u,v)\) 联通当且仅当 \(u,v\) 上均没有棋子),分别表示按照横向坐标为下标的连通块中没有棋子的点集、纵向坐标为下标的连通块中没有棋子的点集、等级为下标的连通块相邻的有白棋的点集、等级为下标的连通块相邻的有黑棋的点集。
- 由于 \(S_4\le 4\),我们直接对于 \((x,y)\) 的上下左右四个点用以上维护出的信息暴力 check 一遍就行了。
复杂度 \(O((nm+q)\log nm)\),但是常数巨大无比。
// Problem: P7963 [NOIP2021] 棋局
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7963
// Memory Limit: 1 MB
// Time Limit: 6000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#define pb emplace_back
#define mt make_tuple
#define mp make_pair
#define fi first
#define se second
// #define FILE
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tu;
bool Mbe;
const int N = 2e5 + 200;
const int M = 1e5 + 100;
const int K = N << 5;
int n, m, q, len, cnt[M];
int fx[4][2] = { { 0, -1 }, { 0, 1 }, { 1, 0 }, { -1, 0 } };
pi lv[M], tp[M];
string s[N], t[N];
vector<pi> st[N];
vector<int> vis[N], id[N], res;
struct qnode {
int c, l, x, y;
qnode () { }
qnode (int _c, int _l, int _x, int _y) :
c(_c), l(_l), x(_x), y(_y) { }
} qr[M];
struct DSU {
int fa[N], mn[N], mx[N];
void init(int lim) {
for (int i = 1; i <= lim; i++)
fa[i] = mn[i] = mx[i] = i;
}
int gf(int x) { return x == fa[x] ? x : fa[x] = gf(fa[x]); }
void mrg(int x, int y) {
if ((x = gf(x)) == (y = gf(y))) return;
fa[x] = y, mx[y] = max(mx[y], mx[x]), mn[y] = min(mn[y], mn[x]);
}
} dx, dy;
struct SEG {
int fa[N], rt[N][4];
struct seg {
int tot, lc[K], rc[K], ct[K];
#define ls lc[x]
#define rs rc[x]
#define mid ((l + r) >> 1)
void upd(int l, int r, int p, int c, int &x) {
if (!x) x = ++tot;
if (l == r) return ct[x] = c, void();
if (p <= mid) upd(l, mid, p, c, ls);
else upd(mid + 1, r, p, c, rs);
ct[x] = ct[ls] + ct[rs];
}
int qry(int l, int r, int s, int t, int x) {
if (!x) return 0;
if (s <= l && r <= t) return ct[x];
if (s > mid) return qry(mid + 1, r, s, t, rs);
else if (t <= mid) return qry(l, mid, s, t, ls);
return qry(l, mid, s, t, ls) + qry(mid + 1, r, s, t, rs);
}
void mrg(int l, int r, int &x, int y) {
if (!x || !y) return x = (x | y), void();
if (l == r) return ct[x] |= ct[y], void();
mrg(l, mid, ls, lc[y]), mrg(mid + 1, r, rs, rc[y]);
ct[x] = ct[ls] + ct[rs];
}
} sx, sy, s0, s1;
void init(int lim) {
for (int i = 1; i <= sx.tot; i++) sx.lc[i] = sx.rc[i] = sx.ct[i] = 0;
for (int i = 1; i <= sy.tot; i++) sy.lc[i] = sy.rc[i] = sy.ct[i] = 0;
for (int i = 1; i <= s0.tot; i++) s0.lc[i] = s0.rc[i] = s0.ct[i] = 0;
for (int i = 1; i <= s1.tot; i++) s1.lc[i] = s1.rc[i] = s1.ct[i] = 0;
sx.tot = sy.tot = s0.tot = s1.tot = 0;
for (int i = 1; i <= lim; i++)
fa[i] = i, rt[i][0] = rt[i][1] = rt[i][2] = rt[i][3] = 0;
}
int gf(int x) { return x == fa[x] ? x : fa[x] = gf(fa[x]); }
void com(int x, int y) {
if ((x = gf(x)) == (y = gf(y))) return;
int tp = x;
if (sx.ct[rt[x][0]] < sx.ct[rt[y][0]]) swap(x, y);
sx.mrg(1, n * m, rt[x][0], rt[y][0]), rt[y][0] = rt[x][0];
if (sy.ct[rt[x][1]] < sy.ct[rt[y][1]]) swap(x, y);
sy.mrg(1, n * m, rt[x][1], rt[y][1]), rt[y][1] = rt[x][1];
if (s0.ct[rt[x][2]] < s0.ct[rt[y][2]]) swap(x, y);
s0.mrg(1, len, rt[x][2], rt[y][2]), rt[y][2] = rt[x][2];
if (s1.ct[rt[x][3]] < s1.ct[rt[y][3]]) swap(x, y);
s1.mrg(1, len, rt[x][3], rt[y][3]), rt[y][3] = rt[x][3];
fa[x] = fa[y] = tp;
}
} T;
int idx(int x, int y) { return (x - 1) * m + y; }
int idy(int x, int y) { return (y - 1) * n + x; }
pi posx(int id) { return mp((id + m - 1) / m, (id - 1) % m + 1); }
pi posy(int id) { return mp((id - 1) % n + 1, (id + n - 1) / n); }
void init() {
dx.init(n * m), dy.init(n * m), T.init(n * m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m - 1; j++)
if (!vis[i][j] && !vis[i][j + 1] && st[i][j].fi == 2)
dx.mrg(idx(i, j), idx(i, j + 1));
for (int i = 1; i <= n - 1; i++)
for (int j = 1; j <= m; j++)
if (!vis[i][j] && !vis[i + 1][j] && st[i][j].se == 2)
dy.mrg(idy(i, j), idy(i + 1, j));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
T.sx.upd(1, n * m, idx(i, j), 1, T.rt[idx(i, j)][0]);
T.sy.upd(1, n * m, idy(i, j), 1, T.rt[idx(i, j)][1]);
if (i > 1 && vis[i - 1][j] && st[i - 1][j].se == 3) {
if (vis[i - 1][j] == 1) T.s0.upd(1, len, id[i - 1][j], 1, T.rt[idx(i, j)][2]);
else T.s1.upd(1, len, id[i - 1][j], 1, T.rt[idx(i, j)][3]);
}
if (i < n && vis[i + 1][j] && st[i][j].se == 3) {
if (vis[i + 1][j] == 1) T.s0.upd(1, len, id[i + 1][j], 1, T.rt[idx(i, j)][2]);
else T.s1.upd(1, len, id[i + 1][j], 1, T.rt[idx(i, j)][3]);
}
if (j > 1 && vis[i][j - 1] && st[i][j - 1].fi == 3) {
if (vis[i][j - 1] == 1) T.s0.upd(1, len, id[i][j - 1], 1, T.rt[idx(i, j)][2]);
else T.s1.upd(1, len, id[i][j - 1], 1, T.rt[idx(i, j)][3]);
}
if (j < m && vis[i][j + 1] && st[i][j].fi == 3) {
if (vis[i][j + 1] == 1) T.s0.upd(1, len, id[i][j + 1], 1, T.rt[idx(i, j)][2]);
else T.s1.upd(1, len, id[i][j + 1], 1, T.rt[idx(i, j)][3]);
}
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m - 1; j++)
if (!vis[i][j] && !vis[i][j + 1] && st[i][j].fi == 3)
T.com(idx(i, j), idx(i, j + 1));
for (int i = 1; i <= n - 1; i++)
for (int j = 1; j <= m; j++)
if (!vis[i][j] && !vis[i + 1][j] && st[i][j].se == 3)
T.com(idx(i, j), idx(i + 1, j));
}
void clear() {
len = 0, res.clear();
for (int i = 1; i <= n; i++)
st[i].clear(), vis[i].clear(), id[i].clear();
for (int i = 1; i <= q; i++) cnt[i] = 0;
}
void solve() {
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) cin >> s[i];
for (int i = 1; i <= n - 1; i++) cin >> t[i];
for (int i = 1; i <= n; i++) {
st[i].resize(m + 1), vis[i].resize(m + 1), id[i].resize(m + 1);
for (int j = 1; j <= m; j++) {
vis[i][j] = 0;
if (j < m) st[i][j].fi = s[i][j - 1] - '0';
if (i < n) st[i][j].se = t[i][j - 1] - '0';
}
}
for (int i = 1, c, l, x, y; i <= q; i++) {
cin >> c >> l >> x >> y, qr[i] = qnode(c, l, x, y), vis[x][y] = 1 + c;
cnt[l]++, lv[i] = tp[++len] = mp(l, cnt[l]);
}
sort(tp + 1, tp + len + 1), len = unique(tp + 1, tp + len + 1) - tp - 1;
for (int i = 1; i <= q; i++) {
qr[i].l = lower_bound(tp + 1, tp + len + 1, lv[i]) - tp;
id[qr[i].x][qr[i].y] = qr[i].l;
}
reverse(qr + 1, qr + q + 1), init();
for (int _ = 1; _ <= q; _++) {
int c = qr[_].c, l = qr[_].l, i = qr[_].x, j = qr[_].y, pr = T.gf(idx(i, j));
if (i > 1 && st[i - 1][j].se == 3) {
int prn = T.gf(idx(i - 1, j));
if (vis[i][j] == 1) T.s0.upd(1, len, id[i][j], 0, T.rt[prn][2]);
else T.s1.upd(1, len, id[i][j], 0, T.rt[prn][3]);
}
if (i < n && st[i][j].se == 3) {
int prn = T.gf(idx(i + 1, j));
if (vis[i][j] == 1) T.s0.upd(1, len, id[i][j], 0, T.rt[prn][2]);
else T.s1.upd(1, len, id[i][j], 0, T.rt[prn][3]);
}
if (j > 1 && st[i][j - 1].fi == 3) {
int prn = T.gf(idx(i, j - 1));
if (vis[i][j] == 1) T.s0.upd(1, len, id[i][j], 0, T.rt[prn][2]);
else T.s1.upd(1, len, id[i][j], 0, T.rt[prn][3]);
}
if (j < m && st[i][j].fi == 3) {
int prn = T.gf(idx(i, j + 1));
if (vis[i][j] == 1) T.s0.upd(1, len, id[i][j], 0, T.rt[prn][2]);
else T.s1.upd(1, len, id[i][j], 0, T.rt[prn][3]);
}
if (j > 1 && !vis[i][j - 1]) {
if (st[i][j - 1].fi == 2) dx.mrg(idx(i, j - 1), idx(i, j));
else if (st[i][j - 1].fi == 3) T.com(idx(i, j - 1), idx(i, j));
}
if (i > 1 && !vis[i - 1][j]) {
if (st[i - 1][j].se == 2) dy.mrg(idy(i - 1, j), idy(i, j));
else if (st[i - 1][j].se == 3) T.com(idx(i - 1, j), idx(i, j));
}
if (j < m && !vis[i][j + 1]) {
if (st[i][j].fi == 2) dx.mrg(idx(i, j), idx(i, j + 1));
else if (st[i][j].fi == 3) T.com(idx(i, j), idx(i, j + 1));
}
if (i < n && !vis[i + 1][j]) {
if (st[i][j].se == 2) dy.mrg(idy(i, j), idy(i + 1, j));
else if (st[i][j].se == 3) T.com(idx(i, j), idx(i + 1, j));
}
int rtx = dx.gf(idx(i, j)), rty = dy.gf(idy(i, j)); pr = T.gf(idx(i, j));
int xmn = posx(dx.mn[rtx]).se, xmx = posx(dx.mx[rtx]).se;
int ymn = posy(dy.mn[rty]).fi, ymx = posy(dy.mx[rty]).fi;
if (xmn > 1 && st[i][xmn - 1].fi == 2 && vis[i][j] + vis[i][xmn - 1] == 3 && id[i][xmn - 1] < id[i][j]) xmn--;
if (xmx < m && st[i][xmx].fi == 2 && vis[i][j] + vis[i][xmx + 1] == 3 && id[i][xmx + 1] < id[i][j]) xmx++;
if (ymn > 1 && st[ymn - 1][j].se == 2 && vis[i][j] + vis[ymn - 1][j] == 3 && id[ymn - 1][j] < id[i][j]) ymn--;
if (ymx < n && st[ymx][j].se == 2 && vis[i][j] + vis[ymx + 1][j] == 3 && id[ymx + 1][j] < id[i][j]) ymx++;
int ans = xmx - xmn + ymx - ymn;
ans += T.sx.ct[T.rt[pr][0]] - 1;
ans -= T.sx.qry(1, n * m, idx(i, xmn), idx(i, xmx), T.rt[pr][0]) - 1;
ans -= T.sy.qry(1, n * m, idy(ymn, j), idy(ymx, j), T.rt[pr][1]) - 1;
if (vis[i][j] == 1) {
ans += T.s1.qry(1, len, 1, id[i][j] - 1, T.rt[pr][3]);
ans -= (xmn != j && vis[i][xmn] == 2 && id[i][xmn] < id[i][j] && T.s1.qry(1, len, id[i][xmn], id[i][xmn], T.rt[pr][3]));
ans -= (xmx != j && vis[i][xmx] == 2 && id[i][xmx] < id[i][j] && T.s1.qry(1, len, id[i][xmx], id[i][xmx], T.rt[pr][3]));
ans -= (ymn != i && vis[ymn][j] == 2 && id[ymn][j] < id[i][j] && T.s1.qry(1, len, id[ymn][j], id[ymn][j], T.rt[pr][3]));
ans -= (ymx != i && vis[ymx][j] == 2 && id[ymx][j] < id[i][j] && T.s1.qry(1, len, id[ymx][j], id[ymx][j], T.rt[pr][3]));
}
if (vis[i][j] == 2) {
ans += T.s0.qry(1, len, 1, id[i][j] - 1, T.rt[pr][2]);
ans -= (xmn != j && vis[i][xmn] == 1 && id[i][xmn] < id[i][j] && T.s0.qry(1, len, id[i][xmn], id[i][xmn], T.rt[pr][2]));
ans -= (xmx != j && vis[i][xmx] == 1 && id[i][xmx] < id[i][j] && T.s0.qry(1, len, id[i][xmx], id[i][xmx], T.rt[pr][2]));
ans -= (ymn != i && vis[ymn][j] == 1 && id[ymn][j] < id[i][j] && T.s0.qry(1, len, id[ymn][j], id[ymn][j], T.rt[pr][2]));
ans -= (ymx != i && vis[ymx][j] == 1 && id[ymx][j] < id[i][j] && T.s0.qry(1, len, id[ymx][j], id[ymx][j], T.rt[pr][2]));
}
auto chk = [&] (int x, int y) {
if (x == i && xmn <= y && y <= xmx) return 0;
if (y == j && ymn <= x && x <= ymx) return 0;
if (vis[x][y] == 0 && T.sx.qry(1, n * m, idx(x, y), idx(x, y), T.rt[pr][0])) return 0;
if (vis[x][y] == 1 && (T.s0.qry(1, len, id[x][y], id[x][y], T.rt[pr][2]) || vis[i][j] == 1 || (vis[i][j] == 2 && id[i][j] <= id[x][y]))) return 0;
if (vis[x][y] == 2 && (T.s1.qry(1, len, id[x][y], id[x][y], T.rt[pr][3]) || vis[i][j] == 2 || (vis[i][j] == 1 && id[i][j] <= id[x][y]))) return 0;
return 1;
};
if (j < m && st[i][j].fi == 1) ans += chk(i, j + 1);
if (j > 1 && st[i][j - 1].fi == 1) ans += chk(i, j - 1);
if (i < n && st[i][j].se == 1) ans += chk(i + 1, j);
if (i > 1 && st[i - 1][j].se == 1) ans += chk(i - 1, j);
res.pb(ans), vis[i][j] = 0;
}
reverse(res.begin(), res.end());
for (int i : res) cout << i << '\n';
clear();
}
bool Med;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cerr << (&Mbe - &Med) / 1048576.0 << " MB\n";
#ifdef FILE
freopen("chess.in", "r", stdin);
freopen("chess.out", "w", stdout);
#endif
int T = 1;
cin >> T;
while (T--) solve();
cerr << (int)(1e3 * clock() / CLOCKS_PER_SEC) << " ms\n";
return 0;
}