CF1606D Red-Blue Matrix

传送门


题意:给一个\(n*m\)的矩阵,让你把一些行涂成红色,剩下的涂成蓝色,但不能都涂红或蓝。问在这个基础上,能否从某一列将矩阵分成两半,使左半矩阵的红色部分的每一个数都大于蓝色部分的每一个数,而右半矩阵的蓝色部分的每一个数都大于红色部分的每一个数。(\(2 \leqslant n,m \leqslant 5 \times 10^5,n \cdot m \leqslant 10^6\)


这题看起来贼吓人,比赛的时候做出来的人竟然还没E题多。我承认我也被吓着了,想了一个很复杂,而且也不好实现的图论相关的算法。

实际上这题很暴力的。最为关键的一点在于,先把每一行按首元素从小到大排序。这样一定是前几行涂成蓝色,后几行涂成红色(否则第一个元素就不满足要求)。然后我们枚举分割矩阵的那一列\(k\),再枚举涂成蓝色的行数\(x\),那么只要判断左半矩阵前\(x\)行的最大值是否小于后\(n - x\)行的最小值,以及右半矩阵前\(x\)行的最小值是否大于后\(n-x\)行的最大值。而这些,可以在\(O(nm)\)下预处理出从四个角向中间延伸的二维前(后)缀最大(小)值。

因此时间复杂度是\(O(n\log n+nm)\)的。


Debug的时候才知道,vector的resize(n, x)是把新开辟出来的空间的初始赋值成x,但是已经开辟出来的元素值不变。

#include<bits/stdc++.h>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 5e5 + 5;
In ll read()
{
	ll ans = 0;
	char ch = getchar(), las = ' ';
	while(!isdigit(ch)) las = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(las == '-') ans = -ans;
	return ans;
}
In void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

int n, m, id[maxn];
vector<vector<int> > a;
vector<vector<int> > lMax, lMin, rMin, rMax; 

In void print(int x, int y)
{
	puts("YES");
	vector<char> ans(n + 1, 'R');
	for(int i = 1; i <= x; ++i) ans[id[i]] = 'B';
	for(int i = 1; i <= n; ++i) putchar(ans[i]);
	space, write(y), enter;
}
In void solve()
{
	for(int j = 1; j < m; ++j)
	{
		for(int i = 1; i < n; ++i)
			if(lMax[i][j] < lMin[i + 1][j] && rMin[i][j + 1] > rMax[i + 1][j + 1])
				return (void)print(i, j);
	}
	puts("NO");
}

int main()
{
	int T = read();
	while(T--)
	{
		n = read(), m = read();
		a.resize(n + 1);
		for(int i = 1; i <= n; ++i) a[i].resize(m + 1), id[i] = i;
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= m; ++j) a[i][j] = read();
		sort(id + 1, id + n + 1, [&](int x, int y) {return a[x][1] < a[y][1];});
		lMax.clear(), lMax.resize(n + 1), lMax[0].resize(m + 1, -INF);
		for(int i = 1; i <= n; ++i)
		{
			lMax[i].resize(m + 1, -INF);
			for(int j = 1; j <= m; ++j) 
				lMax[i][j] = max(a[id[i]][j], max(lMax[i - 1][j], lMax[i][j - 1]));
		}
		lMin.clear(), lMin.resize(n + 2), lMin[n + 1].resize(m + 1, INF);
		for(int i = n; i; --i)
		{
			lMin[i].resize(m + 1, INF);
			for(int j = 1; j <= m; ++j)
				lMin[i][j] = min(a[id[i]][j], min(lMin[i + 1][j], lMin[i][j - 1]));
		}
		rMin.clear(), rMin.resize(n + 1), rMin[0].resize(m + 2, INF);
		for(int i = 1; i <= n; ++i)
		{
			rMin[i].resize(m + 2, INF);
			for(int j = m; j; --j)
				rMin[i][j] = min(a[id[i]][j], min(rMin[i - 1][j], rMin[i][j + 1]));
		}	
		rMax.clear(), rMax.resize(n + 2), rMax[n + 1].resize(m + 2, -INF);
		for(int i = n; i; --i)
		{
			rMax[i].resize(m + 2, -INF);
			for(int j = m; j; --j)
				rMax[i][j] = max(a[id[i]][j], max(rMax[i + 1][j], rMax[i][j + 1]));
		}
		solve();
	}
	return 0;
}
posted @ 2021-11-03 16:15  mrclr  阅读(101)  评论(0编辑  收藏  举报