Loading

【题解】[NOIP2021] 棋局

下午 3 点肝到晚上 7 点,确实毒瘤。

思维难度不是很大?

首先对于 \(0/1\) 边直接特判处理。

观察部分分,如果不存在 \(3\) 边,我们直接用 set 维护每个点只走 \(2\) 能够到达的最右端和最左端即可,非常好写。

如果不存在 \(2\) 边,我们需要维护 \(3\) 边能到达的联通块。

由于每次操作相当与在图上删除一个点,然后维护连通性,非常经典的 trick 是按时间倒序。

我们不仅需要维护联通块内的节点,还要维护与当前联通块有 \(3\) 边直接相连的棋子。那么合并过程就是合并两个集合,然后去重。这也是非常经典的 trick,直接线段树合并或者 set 启发式合并。

现在我们同时有 \(2\) 边和 \(3\) 边,就会多出一种情况,即一个位置同时能通过 \(2\) 边和 \(3\) 边到达。

考虑到 \(2\) 可达的点是连着的一条链,对网格图重标号后就是序列上连续的一段。

所以我们不仅要维护联通块内有多少点,还要维护联通块内具体有哪些点,这也可以用线段树做到。

通过 \(2\) 边吃掉的棋子做多 \(4\) 个,可以用与 \(1\) 边相同的方法处理。

所以对于每个连通块,我们维护四个集合,分别是联通块内横着重标号的点集,竖着重标号的点集,与连通块有 \(3\) 边直接相连的白色棋子的 \(lv\) 集合,和与连通块有 \(3\) 边直接相连的黑色棋子的 \(lv\) 集合。

时间复杂度 \(\mathcal{O}(NM\log NM)\),常数巨大。

/*
    Author : SharpnessV
    Right Output ! & Accepted !
*/
#include<bits/stdc++.h>

#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define pre(i, a, b) for(int i = (a);i >= (b);i--)
#define rp(i, a) for(int i = 1; i <= (a); i++)
#define pr(i, a) for(int i = (a); i >= 1; i--)

#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define si(x) (int)(x).size()
#define el putchar('\n')

using namespace std;
typedef pair<int,int> Pr;
char buf[1<<22],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
template <typename T> inline void read(T &x) {
    x = 0;bool flag = false; char ch = getchar();
    while (ch < '0' || ch > '9')flag = ch == '-' ? true : false, ch = getchar();
    while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + (ch & 15), ch = getchar();
    if(flag) x = -x;
}
#define N 200005
#define M 100005
#define Q 6000006
struct seg{
	int s[Q], ls[Q], rs[Q], idx, rt[N];
	void clear(){
		idx = 0;
		memset(s, 0, sizeof(s));
		memset(ls, 0, sizeof(ls));
		memset(rs, 0, sizeof(rs));
		memset(rt, 0, sizeof(rt));
	}
	void ins(int &x, int l, int r, int pos, int tp = 1){
		if(!x) x = ++idx;
		if(l == r){s[x] = tp; return;}
		int mid = (l + r) >> 1;
		if(mid >= pos)ins(ls[x], l, mid, pos, tp);
		else ins(rs[x], mid + 1, r, pos, tp);
		s[x] = s[ls[x]] + s[rs[x]];
	}
	int merge(int x,int y,int l,int r){
		if(!x || !y)return x | y;
		if(l == r)s[x] = 1;
		else {
			int mid = (l + r) >> 1;
			ls[x] = merge(ls[x], ls[y], l, mid),
			rs[x] = merge(rs[x], rs[y], mid + 1, r);
			s[x] = s[ls[x]] + s[rs[x]];
		}
		return x;
	}
	int ask(int x,int l,int r,int L,int R){
		if(!x)return 0;
		if(L >= l && R <= r)return s[x];
		int mid = (L + R) >> 1, sum = 0;
		if(mid >= l) sum += ask(ls[x], l, r, L, mid);
		if(mid < r)  sum += ask(rs[x], l, r, mid + 1, R);
		return sum;
	}
}id1, id2, col[2];
int n, m, q, v[N], vis[N], vs[N], ed[N], fa[N];
inline int rid(int x,int y){return (x - 1) * m + y;}
inline int cid(int x,int y){return (y - 1) * n + x;}
#define id(x, y) rid(x, y)
#define ck(x, y) (!v[id(x, y)] && !vis[id(x, y)])
vector<int>s[M], t[M], up[M], dn[M], lt[M], rt[M];
Pr st[M];
int get(int x){return fa[x] == x ? x : fa[x] = get(fa[x]);}
struct chess{int lv, x, y, col;}a[M];
void dfs(int, int, int);
void ins(int x,int y,int op){
	if(ck(x, y))dfs(x, y, op);
	else if(v[id(x, y)] && vs[id(x, y)] != op){
		chess w = a[v[id(x, y)]];
		vs[id(x, y)] = op, col[w.col].ins(col[w.col].rt[op], 1, q, w.lv);
	}
}
void dfs(int x,int y,int op){
	vis[id(x, y)] = 1, fa[id(x, y)] = op;
	id1.ins(id1.rt[op], 1, n * m, rid(x, y));
	id2.ins(id2.rt[op], 1, n * m, cid(x, y));
	if(y < m && 3 == s[x][y])    ins(x, y + 1 , op);
	if(y > 1 && 3 == s[x][y - 1])ins(x, y - 1, op);
	if(x < n && 3 == t[x][y])    ins(x + 1, y, op);
	if(x > 1 && 3 == t[x - 1][y])ins(x - 1, y, op);
}
set<int>rw, ln;
void merge(int x,int y){
	if(x == y)return;
	fa[x] = y;
	id1.rt[y] = id1.merge(id1.rt[y], id1.rt[x], 1, n * m);
	id2.rt[y] = id2.merge(id2.rt[y], id2.rt[x], 1, n * m);
	col[0].rt[y] = col[0].merge(col[0].rt[y], col[0].rt[x], 1, q);
	col[1].rt[y] = col[1].merge(col[1].rt[y], col[1].rt[x], 1, q);
}
int calc(int x, int y, int lv, int cl){
	int p = id(x, y); fa[p] = p;
	id1.ins(id1.rt[p], 1, n * m, rid(x, y));
	id2.ins(id2.rt[p], 1, n * m, cid(x, y));
	rw.erase(rid(x, y)), ln.erase(cid(x, y)), v[id(x, y)] = 0;
	
	chess w; 
	if(y < m && 3 == s[x][y] && v[id(x, y + 1)])w = a[v[id(x, y + 1)]],
		col[w.col].ins(col[w.col].rt[p], 1, q, w.lv);
	if(y > 1 && 3 == s[x][y - 1] && v[id(x, y - 1)])w = a[v[id(x, y - 1)]],
		col[w.col].ins(col[w.col].rt[p], 1, q, w.lv);
	if(x < n && 3 == t[x][y] && v[id(x + 1, y)])w = a[v[id(x + 1, y)]],
		col[w.col].ins(col[w.col].rt[p], 1, q, w.lv);
	if(x > 1 && 3 == t[x - 1][y] && v[id(x - 1, y)])w = a[v[id(x - 1, y)]],
		col[w.col].ins(col[w.col].rt[p], 1, q, w.lv);
	
	if(y < m && 3 == s[x][y] && ! v[id(x, y + 1)])merge(get(id(x, y + 1)), p);
	if(y > 1 && 3 == s[x][y - 1] && ! v[id(x, y - 1)])merge(get(id(x, y - 1)), p);
	if(x < n && 3 == t[x][y] && ! v[id(x + 1, y)])merge(get(id(x + 1, y)), p);
	if(x > 1 && 3 == t[x - 1][y] && ! v[id(x - 1, y)])merge(get(id(x - 1, y)), p);
	col[cl].ins(col[cl].rt[p], 1, q, lv, 0);
	// Edge 3
	int ans = id1.s[id1.rt[p]] + col[1 ^ cl].ask(col[1 ^ cl].rt[p], 1, lv - 1, 1, q);
	//cout << "ans " << ans << endl;
	// Edge 1
	if(y < m && 1 == s[x][y]){
		int k = id(x, y + 1);
		if(!v[k])ans += !id1.ask(id1.rt[p], k, k, 1, n * m);
		else if(a[v[k]].col != cl && a[v[k]].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[v[k]].lv, a[v[k]].lv, 1, q);
	}
	if(y > 1 && 1 == s[x][y - 1]){
		int k = id(x, y -  1);
		if(!v[k])ans += !id1.ask(id1.rt[p], k, k, 1, n * m);
		else if(a[v[k]].col != cl && a[v[k]].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[v[k]].lv, a[v[k]].lv, 1, q);
	}
	if(x < n && 1 == t[x][y]){
		int k = id(x + 1, y);
		if(!v[k])ans += !id1.ask(id1.rt[p], k, k, 1, n * m);
		else if(a[v[k]].col != cl && a[v[k]].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[v[k]].lv, a[v[k]].lv, 1, q);
	}
	if(x > 1 && 1 == t[x - 1][y]){
		int k = id(x - 1, y);
		if(!v[k])ans += !id1.ask(id1.rt[p], k, k, 1, n * m);
		else if(a[v[k]].col != cl && a[v[k]].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[v[k]].lv, a[v[k]].lv, 1, q);
	}
	
	// Edge 2
	// left
	int k = max(lt[x][y], y - (rid(x, y) - (*--rw.lower_bound(rid(x, y))) - 1));
	ans += y - k - id1.ask(id1.rt[p], rid(x, k), rid(x, y - 1), 1, n * m);
	if(k > 1 && s[x][k - 1] == 2 && v[id(x, k - 1)]){
		int kk = v[id(x, k - 1)];
		if(a[kk].col != cl && a[kk].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[kk].lv, a[kk].lv, 1, q);
	}
	// right
	k = min(rt[x][y], y + ((*rw.upper_bound(rid(x, y))) - rid(x, y) - 1));
	ans += k - y - id1.ask(id1.rt[p], rid(x, y + 1), rid(x, k), 1, n * m);
	if(k < m && s[x][k] == 2 && v[id(x, k + 1)]){
		int kk = v[id(x, k + 1)];
		if(a[kk].col != cl && a[kk].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[kk].lv, a[kk].lv, 1, q);
	}
	// up
	k = max(up[x][y], x - (cid(x, y) - (*--ln.lower_bound(cid(x, y))) - 1));
	ans += x - k - id2.ask(id2.rt[p], cid(k, y), cid(x - 1, y), 1, n * m);
	if(k > 1 && t[k - 1][y] == 2 && v[id(k - 1, y)]){
		int kk = v[id(k - 1, y)];
		if(a[kk].col != cl && a[kk].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[kk].lv, a[kk].lv, 1, q);
	}
	// down
	k = min(dn[x][y], x + ((*ln.upper_bound(cid(x, y))) - cid(x, y) - 1));
	ans += k - x - id2.ask(id2.rt[p], cid(x + 1, y), cid(k, y), 1, n * m);
	if(k < n && t[k][y] == 2 && v[id(k + 1, y)]){
		int kk = v[id(k + 1, y)];
		if(a[kk].col != cl && a[kk].lv < lv)
			ans += !col[1 ^ cl].ask(col[1 ^ cl].rt[p], a[kk].lv, a[kk].lv, 1, q);
	}
	
	return ans - 1;
}
void clear(){
	memset(a, 0, sizeof(a));
	memset(v, 0, sizeof(v));
	memset(vis, 0, sizeof(vis));
	memset(vs, 0, sizeof(vs));
	memset(ed, 0, sizeof(ed));
	memset(fa, 0, sizeof(fa));
	id1.clear(), id2.clear(), col[0].clear(), col[1].clear();
	rep(i, 0, M - 1)
		s[i].clear(), t[i].clear(), up[i].clear(), dn[i].clear(), lt[i].clear(), rt[i].clear();
	rw.clear(), ln.clear();
}
void solve(){
	clear(), read(n), read(m), read(q);
	rep(i, 1, n){
		s[i].resize(m);
		char ch = getchar();
		while(ch > '3' || ch < '0')ch = getchar();
		rep(j, 1, m - 1)s[i][j] = ch - '0', ch = getchar();
	}
	rep(i, 1, n - 1){
		t[i].resize(m + 1);
		char ch = getchar();
		while(ch > '3' || ch < '0')ch = getchar();
		rep(j, 1, m)t[i][j] = ch - '0', ch = getchar();
	}
	
	rep(i, 1, n){
		up[i].resize(m + 1), lt[i].resize(m + 1);
		rep(j, 1, m){
			if(j > 1 && s[i][j - 1] == 2)lt[i][j] = lt[i][j - 1];
			else lt[i][j] = j;
			if(i > 1 && t[i - 1][j] == 2)up[i][j] = up[i - 1][j];
			else up[i][j] = i;
		}
	}
	pre(i, n, 1){
		dn[i].resize(m + 1), rt[i].resize(m + 1);
		pre(j, m, 1){
			if(j < m && s[i][j] == 2)rt[i][j] = rt[i][j + 1];
			else rt[i][j] = j;
			if(i < n && t[i][j] == 2)dn[i][j] = dn[i + 1][j];
			else dn[i][j] = i;
		}
	}
	rp(i, q){
		read(a[i].col), read(a[i].lv), read(a[i].x), read(a[i].y), 
		v[id(a[i].x, a[i].y)] = i, st[i] = mp(a[i].lv, i);
	}
	sort(st + 1, st + q + 1);
	rp(i, q)a[st[i].se].lv = i;
	rp(i, n)rp(j, m)if(ck(i, j))
		dfs(i, j, id(i, j));
	rw.insert(0), rw.insert(n * m + 1), 
	ln.insert(0), ln.insert(n * m + 1);
	rp(i, q)rw.insert(rid(a[i].x, a[i].y)), ln.insert(cid(a[i].x, a[i].y));
	pr(i, q)ed[i] = calc(a[i].x, a[i].y, a[i].lv, a[i].col);
	rp(i, q)printf("%d\n", ed[i]);
}

int main() {
	int T; read(T); while(T--)solve();
	return 0;
}
posted @ 2021-12-02 19:20  7KByte  阅读(435)  评论(0编辑  收藏  举报