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;
}