Flip and Rectangles

题意

ARC081D
\(n \times m(n,m\leq 2000)\) 的 01 矩阵, 每次可以翻转一行或一列颜色, 求可能的最大 1 子矩阵的面积。

转化

考虑要求一个子矩阵全是 1。

对于其中一点 \((x, y)\), 如果为 0 那么就对这行翻转,使它为 1。

如果 \((x, k), (k, y), k \in Z\) 的格子是 0 就使对应的行或列翻转。

现在 \(x\) 行 和 \(y\) 列的格子都是 1,如果再对子矩阵的任何行列操作都会使它们变化。

这就代表无法操作了。

对于任意 \(i \neq x, j \neq y\), 会可能会被 \(x\) 行的格子翻转或者 \(y\) 列的格子翻转,具体要满足的条件是 \(!a_{x, y} \oplus !a_{x, j}\)\(!a_{i, y}\)

就要满足: \(!a_{x, y} \oplus !a_{x, j} \oplus !a_{i, y} \oplus a_{i,j} = 1\)

等价于: \(1 \oplus a_{x, y} \oplus 1 \oplus a_{x, j} \oplus 1 \oplus a_{i, y} \oplus a_{i, j} = 1 \oplus 1 \oplus 1 \oplus 1\)

即: \(a_{x, y} \oplus a_{x, j} \oplus a_{i, y} \oplus a_{i, j} = 0\)

发现 \(x, y, i, j\) 的取值是较松的,当取到 \(2 \times 2\) 的子矩阵时,只要子矩阵内的所有 \(2 \times 2\) 的子矩阵都满足这个性质,代表它能翻到全黑。

于是对于 \(2 \times 2\) 个点缩成一个点,满足上述限制就为 \(1\), 否则为 \(0\), 就得到 \((n - 1) \times (m - 1)\) 的新矩阵。

问题就转化为是矩形内全是1的矩阵的最大面积。

要注意答案最小也可以是 \(\min{n, m}\)

悬线法

悬线就是上到下的线,考虑左右能最远到的地方。

具体就是当前位置往左往右最大的矩阵。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2050;
const int INF = 0x7fffffff;
const int mod = 1000000007;
using ll = long long;
template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

int n, m;  
char a[MAXN][MAXN];
int b[MAXN][MAXN],  l[MAXN][MAXN], r[MAXN][MAXN], L[MAXN][MAXN], R[MAXN][MAXN], h[MAXN][MAXN]; 
int main() { 
	Read(n); Read(m); 

	for (int i = 1; i <= n; i ++) { 
		cin >> a[i] + 1;  
	}
	
	for (int i = 1; i < n; i ++)
		for (int j = 1; j < m; j ++)
			b[i][j] = ((a[i][j] == '#') + (a[i][j + 1] == '#') + (a[i + 1][j] == '#') + (a[i + 1][j + 1] == '#')) % 2 == 0; 
	int ans = max(n, m); 
	n --, m --; 
		
	for (int i = 1; i <= n; i ++) {
		int lst = 0;
		for (int j = 1; j <= m; j ++)
			if (b[i][j]) l[i][j] = lst;
			else L[i][j] = 0, lst = j;
		lst = m + 1;  
		for (int j = m; j >= 1; j --)
			if (b[i][j]) r[i][j] = lst;
			else R[i][j] = m + 1, lst = j;  
	}
	
	for (int i = 1; i <= m; i ++)
		R[0][i] = m + 1; 
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++) {
			if (b[i][j]) {
				h[i][j] = h[i - 1][j] + 1;
				L[i][j] = max(l[i][j] + 1, L[i - 1][j]);
				R[i][j] = min(r[i][j] - 1, R[i - 1][j]);
				ans = max(ans, (h[i][j] + 1)* (R[i][j] - L[i][j] + 1 + 1));
			}
		}
	cout << ans << endl;
 	return 0;
} 

posted @ 2022-02-17 21:28  qjbqjb  阅读(24)  评论(0编辑  收藏  举报