Atcoder ARC119D Grid Repainting 3

发布时间 2023-07-09 00:36:19作者: lhzawa

看到方格图,便想到把行和列当成图上的点,把红点当作连接行列的边,这样就能构造出二分图。

考虑把每个连通的二分图单独拎出来考虑,能发现对其建一个生成树,只要 \(u\) 节点的操作与 \(son_u\) 的操作相反,则 \(son_u\) 是不会干扰到 \(u\) 的。
这则说明每个连通的二分图消完后肯定是只剩一行或一列。

考虑每个连通的二分图是剩一行或一列能使剩下的白点最少,设图中没有连边的行的数量为 \(a\),没有连边的列的数量为 \(b\),连通二分图剩下的行数为 \(x\),连通二分图剩下的列数为 \(y\),则白点数量即为 \(nm - (a + x)(b + y)\),那就是要 \((a + x)(b + y)\) 最小。
显然 \(a, b\) 是定值,\(x + y\) 是定值,那就是若 \(a\le b\),则全剩下列;若 \(a > b\),则全剩下行。
这部分控制生成树的根节点即可。
\(son_u\) 递归完成后加入 \((u, son_u)\) 的操作保证 \(son_u\) 的操作不会与 \(u\) 的操作矛盾,输出方案时判断一下是消行或消列即可。

// lhzawa(https://www.cnblogs.com/lhzawa/)
// Problem: D - Grid Repainting 3
// Contest: AtCoder - AtCoder Regular Contest 119
// URL: https://atcoder.jp/contests/arc119/tasks/arc119_d
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int N = 2.5e3 + 10;
char s[N][N];
basic_string<int> G[N * 2];
int h[N * 2];
int vis[N * 2];
int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%s", s[i] + 1);
	}
	function<void (int, int)> add = [](int u, int v) -> void {
		G[u] += v;
		return ;
	};
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (s[i][j] == 'R') {
				h[i] = h[n + j] = 1;
				add(i, n + j), add(n + j, i);
			}
		}
	}
	int a = 0, b = 0;
	for (int i = 1; i <= n + m; i++) {
		if (! h[i]) {
			i <= n ? a++ : b++;
		}
	}
	vector<pair<int, int> > opt;
	function<void (int)> dfs = [&dfs, &opt](int u) -> void {
		vis[u] = 1;
		for (int v : G[u]) {
			if (! vis[v]) {
				dfs(v);
				opt.push_back({u, v});
			}
		}
	};
	if (a >= b) {
		for (int i = 1; i <= n; i++) {
			if (! vis[i]) {
				dfs(i);
			}
		}
	}
	else {
		for (int i = n + 1; i <= n + m; i++) {
			if (! vis[i]) {
				dfs(i);
			}
		}
	}
	printf("%zu\n", opt.size());
	for (pair<int, int> i : opt) {
		int x = i.first, y = i.second;
		if (x <= n) {
			printf("Y %d %d\n", x, y - n);
		}
		else {
			printf("X %d %d\n", y, x - n);
		}
	}
	return 0;
}