【BZOJ4651】【UOJ220】【NOI2016】网格

【题目链接】

【思路要点】

  • 当且仅当跳蚤个数不足两个,或跳蚤个数为两个,并且他们位置相邻,答案为-1。
  • 否则,当且仅当原本跳蚤中存在不连通的跳蚤,答案为0。
  • 否则,当且仅当原图中存在割点,答案为1。
  • 否则答案为2。
  • 将整张图建出来,依次检验以上判断标准,可以得到一个时空复杂度为\(O(N*M)\)的做法。
  • 我们发现\(N\)和\(M\)均很大,而\(C\)很小,这意味着存在大面积的空白,考虑只考虑周围两圈存在障碍点的位置。
  • 显然上述位置的数量是\(O(C)\)的,我们在相邻的被选中的点之间对连边。
  • 我们称周围一圈存在障碍点的位置为1级位置,其余被选中的位置为2级位置。
  • 原图中存在不连通的跳蚤等价于新图的每个联通块(算上蛐蛐)中存在不连通的跳蚤。
  • [1]若原图(\(N*M\)的图)存在割点,那么这些割点的位置必定是1级位置,而由于我们选定了所有2级位置,1级位置在原图中是割点等价于1级位置在新图中是割点。
  • 在新图上运行Tarjan算法,判断是否存在割点。
  • 时间复杂度\(O(\sum C)\)。
  • 实现时需要特判\(N\)或\(M\)为1的情况,因为此时[1]不成立。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXP = 3000005;
const int P = 1e6 + 3;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, m, k;
struct HashTable {
	int nxt[MAXP], head[P];
	int x[MAXP], y[MAXP], l[MAXP], tot;
	bool vis[MAXP];
	void init() {
		for (int i = 1; i <= tot; i++)
			head[(x[i] * (m + 1ll) + y[i]) % P] = 0;
		tot = 0;
	}
	void insert(int tx, int ty, bool val, int level) {
		if (tx < 1 || tx > n) return;
		if (ty < 1 || ty > m) return;
		int r = (tx * (m + 1ll) + ty) % P, p = head[r];
		while (p != 0) {
			if (x[p] == tx && y[p] == ty) {
				chkmin(l[p], level);
				return;
			}
			p = nxt[p];
		}
		tot++;
		x[tot] = tx;
		y[tot] = ty;
		l[tot] = level;
		vis[tot] = val;
		nxt[tot] = head[r];
		head[r] = tot;
	}
	int query(int tx, int ty) {
		if (tx < 1 || tx > n) return -1;
		if (ty < 1 || ty > m) return -1;
		int r = (tx * (m + 1ll) + ty) % P, p = head[r];
		while (p != 0) {
			if (x[p] == tx && y[p] == ty) return p;
			p = nxt[p];
		}
		return -1;
	}
} HT;
vector <int> a[MAXP];
int timer, dfn[MAXP], low[MAXP];
bool vis[MAXP], found; int f, g, h;
void fill(int pos) {
	vis[pos] = true; f++; g += !HT.vis[pos];
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (!vis[a[pos][i]]) fill(a[pos][i]);
}
void tarjan(int pos, int fa) {
	h++;
	dfn[pos] = low[pos] = ++timer;
	int tmp = 0;
	for (unsigned i = 0; i < a[pos].size(); i++) {
		if (HT.vis[a[pos][i]]) continue;
		if (dfn[a[pos][i]] == 0) {
			tarjan(a[pos][i], pos);
			chkmin(low[pos], low[a[pos][i]]);
			if (fa == 0) tmp++;
			if (low[a[pos][i]] == dfn[pos] && fa != 0) {
				found |= HT.l[pos] == 1;
			}
		} else chkmin(low[pos], dfn[a[pos][i]]);
	}
	if (tmp >= 2) found |= HT.l[pos] == 1;
}
int main() {
	int T; read(T);
	HT.init();
	while (T--) {
		read(n), read(m), read(k);
		if (k == 0) {
			if (n + m <= 3) printf("-1\n");
			else if (n == 1 || m == 1) printf("1\n");
			else printf("2\n");
			continue;
		}
		if (n == 1) {
			static int pos[MAXP];
			for (int i = 1; i <= k; i++) {
				int x, y;
				read(x), read(y);
				pos[i] = y;
			}
			sort(pos + 1, pos + k + 1);
			pos[k + 1] = m + 1;
			if (m - k <= 1) {
				printf("-1\n");
				continue;
			}
			int Max = 0, cnt = 0;
			for (int i = 0; i <= k; i++) {
				chkmax(Max, pos[i + 1] - pos[i] - 1);
				if (pos[i + 1] - pos[i] - 1) cnt++;
			}
			if (m - k == 2 && Max == 2) {
				printf("-1\n");
				continue;
			}
			if (cnt >= 2) {
				printf("0\n");
				continue;
			}
			printf("1\n");
			continue;
		}
		if (m == 1) {
			static int pos[MAXP];
			for (int i = 1; i <= k; i++) {
				int x, y;
				read(x), read(y);
				pos[i] = x;
			}
			sort(pos + 1, pos + k + 1);
			pos[k + 1] = n + 1;
			if (n - k <= 1) {
				printf("-1\n");
				continue;
			}
			int Max = 0, cnt = 0;
			for (int i = 0; i <= k; i++) {
				chkmax(Max, pos[i + 1] - pos[i] - 1);
				if (pos[i + 1] - pos[i] - 1) cnt++;
			}
			if (n - k == 2 && Max == 2) {
				printf("-1\n");
				continue;
			}
			if (cnt >= 2) {
				printf("0\n");
				continue;
			}
			printf("1\n");
			continue;
		}
		for (int i = 1; i <= k; i++) {
			int x, y;
			read(x), read(y);
			HT.insert(x, y, true, 0);
		}
		if (1ll * n * m - k <= 1) {
			printf("-1\n");
			HT.init();
			continue;
		}
		if (1ll * n * m - k == 2) {
			int tx = 0, ty = 0, sx = 0, sy = 0;
			for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++) {
				if (HT.query(i, j) == -1) {
					if (tx == 0) {
						tx = i;
						ty = j;
					} else {
						sx = i;
						sy = j;
					}
				}
			}
			if (abs(tx - sx) + abs(ty - sy) == 1) {
				printf("-1\n");
				HT.init();
				continue;
			}
		}
		for (int i = 1; i <= k; i++) {
			int tx = HT.x[i];
			int ty = HT.y[i];
			for (int dx = -2; dx <= 2; dx++)
			for (int dy = -2; dy <= 2; dy++) {
				int tmp = max(abs(dx), abs(dy));
				HT.insert(tx + dx, ty + dy, false, tmp);
			}
		}
		timer = 0;
		for (int i = 1; i <= HT.tot; i++) {
			a[i].clear();
			vis[i] = false;
			dfn[i] = low[i] = 0;
		}
		for (int i = 1; i <= HT.tot; i++) {
			int tx = HT.x[i];
			int ty = HT.y[i];
			for (int dx = -1; dx <= 1; dx++)
			for (int dy = -1; dy <= 1; dy++) {
				if (dx * dy != 0) continue;
				int tmp = HT.query(tx + dx, ty + dy);
				if (tmp != -1 && tmp != i) {
					a[i].push_back(tmp);
				}
			}
		}
		int ans = 2;
		for (int i = k + 1; i <= HT.tot; i++) {
			if (!vis[i]) {
				f = g = 0;
				fill(i);
				h = 0;
				found = false;
				tarjan(i, 0);
				if (h != g) chkmin(ans, 0);
				if (found) chkmin(ans, 1);
			}
		}
		printf("%d\n", ans);
		HT.init();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80499234