信息学奥赛一本通提高组2085:【21NOIP提高组】棋局

【题目描述】

在输了一晚上的麻将之后,小 z 和小 c 卸掉了手机上的所有牌类游戏。不过这怎么可能阻挡得了他们上课颓废的决心呢?现在他们的目光盯在了棋类游戏上,但他们两个除了天天下飞行器以外,几乎所有棋类游戏都只懂个大概规则。

“既然我们都会玩但只能玩一点点,不如我们自己搞个缝合怪出来吧!”

于是,在他们的精心脑洞之下,一个融合了围棋、象棋与军棋的奇妙游戏诞生了……

游戏在一张长 n 行宽 m 列的网格形棋盘上进行,棋子落在网格的交叉点上。我们不妨记左上角的交叉点的坐标为 (1, 1) ,右下角的交叉点坐标为 (n, m) 。

棋子分为黑白两色,对局双方各执一方棋子。

每个棋子除了颜色以外还有等级,不妨设 coli 为棋子 i 的颜色,lvi 为棋子 i 的等级。另外,棋盘上的网格线共有 4 种状态,对于第 i 条网格线,设其状态为 opti 。

轮到每方下棋时,他可以选择棋盘上的一个己方棋子沿网格线进行移动到另一个交叉点,称为走子。形式化定义走子的过程如下:选择一个坐标序列 (x0, y0),(x1, y1), ...,(xk, yk),其中 k 是任意选定的正整数,(x0, y0) 是棋子初始的位置,(xk, yk) 是棋子最终走到的位置,需要满足:

• 对于任意 i=0,1,...,k−1 ,坐标 (xi, yi) 和 (xi+1, yi+1) 之间必须有网格线直接相连,也就是说走子必须沿着网格线走 ;

• 对于任意 i ≠ j ,必须有 (xi, yi) ≠  (xj, yj ) ,也就是说走子过程中不能经过重复位置,特别地 (x0, y0)≠ (xk, yk) ,也就是说不能原地不动(或走回原地) 。

• 对于任意 i=1,...,k−1,坐标 (xi, yi) 上必须没有棋子,也就是说走子时不能越过已有的棋子 。

• 若 (xk, yk) 上没有棋子,称为普通走子,否则称为吃子。在吃子过程中,设正在走的棋子颜色为 col1 ,等级为 lv1 ,被吃的棋子颜色为 col2 ,等级为lv2 ,则必须满足 col1 ≠ col2, lv1 ≥ lv2 ,换句话说只能吃与自己颜色不同, 且等级不高于自己等级的 棋子 。

需要注意的是,由上述定义可以得出,不允许棋子在吃子后继续向前走。

网格线的状态含义如下所述:

• 如果 opti=0 ,代表此路不通,走子时不能经过这条网格线。

• 如果 opti=1 ,代表这条网格线是一条“普通道路”,每次走子时棋子最多只能经过 1 条普通道路。

• 如果 opti=2 ,代表这条网格线是一条“直行道路”,每次走子时棋子可以经过任意条直行道路,但只能一直沿横向或一直沿纵向走,不能转弯。如沿直行道路从(1,1) 经过 (1,2) 走到 (1,3) 是可以的,但是从 (1,1) 经过 (1,2) 走到 (2,2) 不行。

• 如果 opti=3 ,代表这条网格线是一条“互通道路”,每次走子时棋子可以经过任意条互通道路,且中途可任意转弯。

同时规定在一次走子过程中,棋子经过的网格线的状态必须全部相同,比如从 (1,1)经过直行道路走到 (1,2) 再经过互通道路走到 (1,3) 是不允许的。

至于如何判断胜负等其他细节,与本题无关,故略去。

小 z 和小 c 开发出这款棋类游戏后,为了提升水平,想了一个训练的策略:一开始棋盘是空的,然后小 c 会每次往棋盘的某个空交叉点上放一枚棋子,小 z 需要快速计算出:若选择这枚新放上的棋子进行一次走子,棋盘上一共有多少个位置是能被走到的?注意,因为这只是思维训练,他们并不会真的走这枚棋子。

可怜的小 z 发现他的计算力不足以算出这个问题,只好向你求助。

【输入】

每个测试点由多组数据组成。

第一行:一个正整数 T 表示数据组数。

对于每组数据:

第一行:3 个正整数 n, m, q ,分别表示棋盘的行数、列数和游戏的轮数。

接下来 n 行,每行为一个长 m−1 的字符串,每个字符为 0、1、2、3 中的一个,第 i 行第 j 个字符表示交叉点 (i,j) 连向交叉点 (i,j+1) 的网格线状态。

接下来 n−1 行,每行为一个长 m 的字符串,每个字符为 0、1、2、3 中的一个,第 i 行第 j 个字符表示交叉点 (i,j) 连向交叉点 (i+1,j) 的网格线状态。

接下来 q 行,每行 4 个非负整数 coli,lvi,xi,yi ,表示在第 i 轮有一枚颜色为 coli ,

等级为 lvi 的棋子放在了交叉点 (xi, yi) 上。其中 coli=0 表示黑子,coli=1 表示白子。

保证之前交叉点 (xi, yi) 上没有棋子。

【输出】

对于每组数据输出 q 行,每行一个非负整数,表示第 i 枚棋子放置后能走到的交叉点数量。

【输入样例】

1
3 3 5
13
22
23
010
233
0 1 2 3
1 2 2 1
1 3 1 2
0 2 3 2
1 3 2 2

【输出样例】

4
3
3
3
2

【提示】

【样例 1 解释】

放置棋子 1 后,它能走到的位置为 (2, 1),(2, 2),(3, 2),(3, 3) 。

放置棋子 2 后,它能走到的位置为 (2, 2),(2, 3),(3, 1) 

放置棋子 3 后,它能走到的位置为 (1, 1),(1, 3),(2, 2) 

放置棋子 4 后,它能走到的位置为 (2, 2),(3, 1),(3, 3) 

放置棋子 5 后,它能走到的位置为 (2, 3),(3, 2) 

【样例 2 输入】

2
2 3 4
22
33
123
0 2 1 2
0 1 2 1
1 2 1 3
0 3 2 2
3 2 3
3
1
3
32
32
0 2 1 2
1 2 3 2
0 1 2 2

【样例 2 输出】

3
4
4
2
5
5
1

【数据范围】

测试点编号n × m ≤q ≤特殊性质
1 ∼ 210050
3 ∼ 650002000
7 ∼ 82 × 105105不存在“直行道路”与“互通道路”
9 ∼ 11不存在“互通道路”
12 ∼ 14不存在“直行道路”
15 ∼ 16lvi = i
17 ∼ 18lvi = q − i + 1
19 ∼ 212000n, m ≤ 1000
22 ∼ 25105

对于 100% 的数据,1≤T≤5 ,2≤n,m≤105 ,4≤n×m≤2×105 ,1≤q≤min{105, n×m} ,1≤lvi≤q ,1≤xi≤n ,1≤yi≤m ,coli ∈ {0, 1} 。

注:由于本题输入输出规模较大,建议使用较为快速的输入输出方式。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5;
#define x first
#define y second
#define pii pair<int,int>
int n, m, q, idlim;

int idh(int x, int y) {
	return x * (m + 2) + y;
}

pii hdi(int id) {
	return pii(id / (m + 2), id % (m + 2));
}

int idv(int x, int y) {
	return y * (n + 2) + x;
}

pii vdi(int id) {
	return pii(id % (n + 2), id / (n + 2));
}
template<class T>

struct array2d {
	T a[N];
	int n, m;
	void init(int val = 0) {
		memset(a, val, sizeof(a));
	}
	void set_size(int n, int m, int val = 0) {
		this->n = n;
		this->m = m;
		init(val);
	}
	T *operator[](int i) {
		return a + i * (m + 2);
	}
	const T *operator[](int i)const {
		return a + i * (m + 2);
	}
	T &operator[](pii p) {
		return a[p.x * (m + 2) + p.y];
	}
	const T &operator[](pii p)const {
		return a[p.x * (m + 2) + p.y];
	}
};
array2d<int> v, h, col, lv, tim;
vector<pii> piece;

struct node {
	int ch[2], sz;
};
node t[N * 30];
int cnt;

void pushup(int x) {
	t[x].sz = t[t[x].ch[0]].sz + t[t[x].ch[1]].sz;
}

void insert(int p, int &x, int l, int r) {
	if (!x)
		x = ++cnt;
	if (l == r) {
		t[x].sz = 1;
		return;
	}
	int mid = (l + r) >> 1;
	if (p <= mid)
		insert(p, t[x].ch[0], l, mid);
	else
		insert(p, t[x].ch[1], mid + 1, r);
	pushup(x);
}

void erase(int p, int &x, int l, int r) {
	if (!x)
		return;
	if (l == r) {
		t[x].sz = 0;
		return;
	}
	int mid = (l + r) >> 1;
	if (p <= mid)
		erase(p, t[x].ch[0], l, mid);
	else
		erase(p, t[x].ch[1], mid + 1, r);
	pushup(x);
}

int merge(int x, int y) {
	if (!(x && y))
		return x + y;
	t[x].ch[0] = merge(t[x].ch[0], t[y].ch[0]);
	t[x].ch[1] = merge(t[x].ch[1], t[y].ch[1]);
	if (t[x].ch[0] || t[x].ch[1])
		pushup(x);
	else
		t[x].sz |= t[y].sz;
	return x;
}

int query_rk(int v, int x, int l, int r) {
	if (!t[x].sz)
		return 0;
	if (l == r)
		return t[x].sz;
	int mid = (l + r) >> 1;
	if (v <= mid)
		return query_rk(v, t[x].ch[0], l, mid);
	else
		return t[t[x].ch[0]].sz + query_rk(v, t[x].ch[1], mid + 1, r);
}

bool exist(int v, int x, int l, int r) {
	if (!t[x].sz)
		return 0;
	if (l == r)
		return 1;
	int mid = (l + r) >> 1;
	if (v <= mid)
		return exist(v, t[x].ch[0], l, mid);
	else
		return exist(v, t[x].ch[1], mid + 1, r);
}

struct DSU_with_lr {
	int l[N], r[N], f[N];
	int _(int x) {
		return f[x] == x ? x : f[x] = _(f[x]);
	}
	void merge(int x, int y) {
		x = _(x), y = _(y);
		if (x == y)
			return;
		f[x] = y;
		l[y] = min(l[y], l[x]);
		r[y] = max(r[y], r[x]);
	}
	int getl(int id) {
		return l[_(id)];
	}
	int getr(int id) {
		return r[_(id)];
	}
	bool check(int x, int y) {
		return _(x) == _(y);
	}
};
DSU_with_lr hseg, vseg;

struct block {
	int rt0, rt1, rth, rtv;
	void merge(block &y) {
		rt0 =::merge(rt0, y.rt0);
		rt1 =::merge(rt1, y.rt1);
		rth =::merge(rth, y.rth);
		rtv =::merge(rtv, y.rtv);
	}
	int get0(int v) {
		return query_rk(v, rt0, 1, q);
	}
	void ins0(int v) {
		insert(v, rt0, 1, q);
	}
	void era0(int v) {
		erase(v, rt0, 1, q);
	}
	int get1(int v) {
		return query_rk(v, rt1, 1, q);
	}
	void ins1(int v) {
		insert(v, rt1, 1, q);
	}
	void era1(int v) {
		erase(v, rt1, 1, q);
	}
	void ins01(int col, int v) {
		col ? ins1(v) : ins0(v);
	}
	void era01(int col, int v) {
		col ? era1(v) : era0(v);
	}
	void insp(int x, int y) {
		insert(idh(x, y), rth, 1, idlim);
		insert(idv(x, y), rtv, 1, idlim);
	}
	int getsz(int col, int lv) {
		return t[rth].sz + (1 - col ? get1(lv) : get0(lv));
	}
	int geth(int x, int y1, int y2, int colx, int lvx) {
		auto check = [&](int p, int q) {
			return col[p][q] == 1 - colx && lv[p][q] <= lvx && exist(lv[p][q], 1 - colx ? rt1 : rt0, 1, ::q);
		};
		return query_rk(idh(x, y2), rth, 1, idlim) - query_rk(idh(x, y1 - 1), rth, 1, idlim)
		       + (h[x][y1 - 1] == 2 && check(x, y1 - 1)) + (h[x][y2] == 2 && check(x, y2 + 1));
	}
	int getv(int x1, int x2, int y, int colx, int lvx) {
		auto check = [&](int p, int q) {
			return col[p][q] == 1 - colx && lv[p][q] <= lvx && exist(lv[p][q], 1 - colx ? rt1 : rt0, 1, ::q);
		};
		return query_rk(idv(x2, y), rtv, 1, idlim) - query_rk(idv(x1 - 1, y), rtv, 1, idlim)
		       + (v[x1 - 1][y] == 2 && check(x1 - 1, y)) + (v[x2][y] == 2 && check(x2 + 1, y));
	}
};
template<class T>

struct DSU_array2d {
	array2d<T>a;
	array2d<pii>f;
	void init(int val = 0) {
		a.init(val);
	}
	void set_size(int n, int m, int val = 0) {
		a.set_size(n, m, val);
		f.set_size(n, m, val);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				f[i][j] = {i, j};
	}
	pii _(pii x) {
		return x == f[x] ? x : f[x] = _(f[x]);
	}
	T &operator[](pii p) {
		return a[_(p)];
	}
	void merge(pii x, pii y) {
		x = _(x), y = _(y);
		if (x == y)
			return;
		a[x].merge(a[y]);
		f[y] = x;
	}
};
DSU_array2d<block> b;

int qsum = 0;
int ans[N];
int lvcnt[N];
void mian() {
	cin >> n >> m >> q;
	v.set_size(n, m);
	h.set_size(n, m);
	col.set_size(n, m, -1);
	lv.set_size(n, m);
	tim.set_size(n, m);
	piece.clear();
	memset(t, 0, sizeof(t));
	memset(&hseg, 0, sizeof(hseg));
	memset(&vseg, 0, sizeof(vseg));
	memset(lvcnt, 0, sizeof(lvcnt));
	b.set_size(n, m);
	for (int i = 0; i < N; i++)
		hseg.l[i] = hseg.r[i] = hseg.f[i] = i;
	for (int i = 0; i < N; i++)
		vseg.l[i] = vseg.r[i] = vseg.f[i] = i;
	idlim = (n + 3) * (m + 3);

	cnt = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j < m; j++) {
			char c;
			cin >> c;
			h[i][j] = c - '0';
		}
	for (int i = 1; i < n; i++)
		for (int j = 1; j <= m; j++) {
			char c;
			cin >> c;
			v[i][j] = c - '0';
		}
	for (int i = 1; i <= q; i++) {
		int colx, lvx, x, y;
		cin >> colx >> lvx >> x >> y;
		col[x][y] = colx;
		lv[x][y] = lvx;
		++lvcnt[lvx];
		tim[x][y] = i;
		piece.push_back(pii(x, y));
	}
	for (int i = 1; i <= q; i++)
		lvcnt[i] += lvcnt[i - 1];
	for (int i = q - 1; i >= 0; --i)
		lv[piece[i].x][piece[i].y] = lvcnt[lv[piece[i].x][piece[i].y]]--;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j < m; j++) {
			if (h[i][j] == 2 && !lv[i][j] && !lv[i][j + 1])
				hseg.merge(idh(i, j), idh(i, j + 1));
			if (h[i][j] == 3 && !lv[i][j] && !lv[i][j + 1])
				b.merge({i, j}, {i, j + 1});
			if (h[i][j] == 3 && lv[i][j] && !lv[i][j + 1]) {
				b[ {i, j + 1}].ins01(col[i][j], lv[i][j]);
			}
			if (h[i][j] == 3 && !lv[i][j] && lv[i][j + 1]) {
				b[ {i, j}].ins01(col[i][j + 1], lv[i][j + 1]);
			}
		}
	}
	for (int i = 1; i < n; i++) {
		for (int j = 1; j <= m; j++) {
			if (v[i][j] == 2 && !lv[i][j] && !lv[i + 1][j])
				vseg.merge(idv(i, j), idv(i + 1, j));
			if (v[i][j] == 3 && !lv[i][j] && !lv[i + 1][j])
				b.merge({i, j}, {i + 1, j});
			if (v[i][j] == 3 && lv[i][j] && !lv[i + 1][j]) {
				b[ {i + 1, j}].ins01(col[i][j], lv[i][j]);
			}
			if (v[i][j] == 3 && !lv[i][j] && lv[i + 1][j]) {
				b[ {i, j}].ins01(col[i + 1][j], lv[i + 1][j]);
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (!lv[i][j]) {
				b[ {i, j}].insp(i, j);
			}
		}
	}
	for (int i = q - 1; i >= 0; --i) {
		int ans = -1;
		int x = piece[i].x, y = piece[i].y;
		int lvx = lv[x][y], colx = col[x][y];
		lv[x][y] = 0;
		col[x][y] = -1;

		if (h[x][y] == 2 && !lv[x][y] && !lv[x][y + 1])
			hseg.merge(idh(x, y), idh(x, y + 1));
		if (h[x][y - 1] == 2 && !lv[x][y - 1] && !lv[x][y])
			hseg.merge(idh(x, y - 1), idh(x, y));
		pii l = hdi(hseg.getl(idh(x, y))), r = hdi(hseg.getr(idh(x, y)));
		ans += r.y - l.y + 1;

		if (v[x][y] == 2 && !lv[x][y] && !lv[x + 1][y])
			vseg.merge(idv(x, y), idv(x + 1, y));
		if (v[x - 1][y] == 2 && !lv[x - 1][y] && !lv[x][y])
			vseg.merge(idv(x - 1, y), idv(x, y));
		pii u = vdi(vseg.getl(idv(x, y))), d = vdi(vseg.getr(idv(x, y)));
		ans += d.x - u.x + 1;

		b[ {x, y}].insp(x, y);
		if (h[x][y] == 3) {
			if (!lv[x][y + 1])
				b.merge({x, y}, {x, y + 1});
			else
				b[ {x, y}].ins01(col[x][y + 1], lv[x][y + 1]);
		}
		if (h[x][y - 1] == 3) {
			if (!lv[x][y - 1])
				b.merge({x, y - 1}, {x, y});
			else
				b[ {x, y}].ins01(col[x][y - 1], lv[x][y - 1]);
		}
		if (v[x][y] == 3) {
			if (!lv[x + 1][y])
				b.merge({x, y}, {x + 1, y});
			else
				b[ {x, y}].ins01(col[x + 1][y], lv[x + 1][y]);
		}
		if (v[x - 1][y] == 3) {
			if (!lv[x - 1][y])
				b.merge({x - 1, y}, {x, y});
			else
				b[ {x, y}].ins01(col[x - 1][y], lv[x - 1][y]);
		}
		b[ {x, y}].era01(colx, lvx);
		ans += b[ {x, y}].getsz(colx, lvx);
		ans -= b[ {x, y}].geth(l.x, l.y, r.y, colx, lvx);
		ans -= b[ {x, y}].getv(u.x, d.x, u.y, colx, lvx);

		if (col[l.x][l.y - 1] == 1 - colx && h[l.x][l.y - 1] == 2 && lv[l.x][l.y - 1] <= lvx)
			++ans, --l.y;
		if (col[r.x][r.y + 1] == 1 - colx && h[r.x][r.y  ] == 2 && lv[r.x][r.y + 1] <= lvx)
			++ans, ++r.y;
		if (col[u.x - 1][u.y] == 1 - colx && v[u.x - 1][u.y] == 2 && lv[u.x - 1][u.y] <= lvx)
			++ans, --u.x;
		if (col[d.x + 1][d.y] == 1 - colx && v[d.x  ][d.y] == 2 && lv[d.x + 1][d.y] <= lvx)
			++ans, ++d.x;

		auto check = [&](int p, int q) {
			return !lv[p][q] || (col[p][q] == 1 - colx && lv[p][q] <= lvx);
		};
		auto vis = [&](int p, int q) {
			if (b._({p, q}) == b._({x, y}))
				return 1;
			if (lv[p][q] && exist(lv[p][q], col[p][q] ? b[ {x, y}].rt1: b[ {x, y}].rt0, 1, ::q))
				return 1;
			return 0;
		};
		if (h[x][y] == 1 && check(x, y + 1) && !vis(x, y + 1))
			++ans;
		if (v[x][y] == 1 && check(x + 1, y) && !vis(x + 1, y))
			++ans;
		if (h[x][y - 1] == 1 && check(x, y - 1) && !vis(x, y - 1))
			++ans;
		if (v[x - 1][y] == 1 && check(x - 1, y) && !vis(x - 1, y))
			++ans;
		::ans[i] = ans;
	}
	for (int i = 0; i < q; i++)
		cout << ans[i] << "\n";
	qsum += q;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	int T;
	cin >> T;
	while (T--) {
		mian();
	}
}

提交,答案正确。

 

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值