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;
}
posted @ 2023-10-12 16:21  Ender_32k  阅读(97)  评论(0编辑  收藏  举报