题解
发现标签是单调队列,我也不会单调队列,所以写 K-Dtree。
正文
一句话题意(迫真)
求所有满足矩形区域内最大值和最小值的差不大于 \(lim\) 的矩形区域的面积的最大值。
解析
那么思路就很清晰了。
我们发现 \(n\) 很小,所以直接两层循环枚举矩形的高,而 \(m\),也就是宽,结合简单的最优性策略,我们可以用双指针一遍扫过去。
那么枚举矩形就做到了 \(O(n^2m)\) 的复杂度。
然后就是如何得到矩形区域的最大最小值$。
这个很显然吧,直接 K-Dtree 维护矩形最大最小值就可以做了。
点数最多是 \(nm\) 的,这一步的复杂度可以很容易做到 \(O(\log nm)\)。
然后就可以拼起来了,总复杂度 \(O(n^2m \log nm)\),思路很简单,码略长。
代码
// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
using namespace std;
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')
using pii = pair<int, int>;
template<class T> inline T in() {
T n = 0; char p = getc();
while (p < '-') p = getc();
bool f = p == '-' ? p = getc() : 0;
do n = n * 10 + (p ^ 48), p = getc();
while (isdigit(p));
return f ? -n : n;
return n;
}
template<class T> inline T in(T &a) { return a = in<T>(); }
template<class T> inline void out(T n) {
if(n < 0) putc('-'), n = -n;
if(n > 9) out(n / 10);
putc(n % 10 + '0');
}
template<class T1, class T2> T1 max(T1 a, T2 b) { return a > b ? a : a = b;}
template<class T1, class T2> T1 min(T1 a, T2 b) { return a < b ? a : a = b;}
const int N = 1e5 + 10;
struct pot { // 完全就是 K-Dtree 的模板,有什么不会的自行 oi-wiki
int x[2], val;
} p[N * 2];
struct node {
int mi[2], mx[2], val[2]; // 需要维护最大最小值
int l, r;
pot p;
} t[N * 2];
inline void up(int u) {
int l = t[u].l, r = t[u].r;
for(int i : {0, 1}) {
t[u].mi[i] = t[u].mx[i] = t[u].p.x[i];
t[u].val[0] = t[u].val[1] = t[u].p.val;
if(l) {
t[u].mi[i] = min(t[u].mi[i], t[l].mi[i]);
t[u].mx[i] = max(t[u].mx[i], t[l].mx[i]);
t[u].val[0] = min(t[u].val[0], t[l].val[0]);
t[u].val[1] = max(t[u].val[1], t[l].val[1]);
}
if(r) {
t[u].mi[i] = min(t[u].mi[i], t[r].mi[i]);
t[u].mx[i] = max(t[u].mx[i], t[r].mx[i]);
t[u].val[0] = min(t[u].val[0], t[r].val[0]);
t[u].val[1] = max(t[u].val[1], t[r].val[1]);
}
}
}
int cnt;
inline void build(int&u, int l, int r, int wd) { // 建树 (我在写什么注释啊啊啊,完全不知道应该写什么注释)
if(l > r) return;
int m = (l + r) >> 1;
nth_element(p + l, p + m, p + r + 1, [&](pot a, pot b) { return a.x[wd] < b.x[wd]; });
t[u = ++ cnt].p = p[m];
build(t[u].l, l, m - 1, wd ^ 1);
build(t[u].r, m + 1, r, wd ^ 1);
up(u);
}
inline bool inr(node p, int mi[2], int ma[2]) { // in range
return p.mi[0] >= mi[0] and p.mx[0] <= ma[0] and p.mi[1] >= mi[1] and p.mx[1] <= ma[1];
}
inline bool outr(node p, int mi[2], int mx[2]) { // out of range
return p.mi[0] > mx[0] || p.mx[0] < mi[0] || p.mi[1] > mx[1] || p.mx[1] < mi[1];
}
inline bool inr(pot p, int mi[2], int ma[2]) { // in range
return p.x[0] <= ma[0] and p.x[0] >= mi[0] and p.x[1] <= ma[1] and p.x[1] >= mi[1];
}
inline pii query(int u, int mi[2], int ma[2]) { // 询问
if(!u) return {200000, -200000};
if(inr(t[u], mi, ma)) return {t[u].val[0], t[u].val[1]};
if(outr(t[u], mi, ma)) return{200000, -200000};
pii l = query(t[u].l, mi, ma), r = query(t[u].r, mi, ma);
if(inr(t[u].p, mi, ma)) return {min({l.first, r.first, t[u].p.val}), max({l.second, r.second, t[u].p.val})}; // 别忘了单点
return {min(l.first, r.first), max(l.second, r.second)};
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("i.ru", "r", stdin);
freopen("o", "w", stdout);
#endif
int n = in(n), m = in(m), cnp = 0, rt = 0;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
p[++ cnp] = {{i, j}, in<int>()};
}
}
build(rt, 1, cnp, 0);
int mi[2], ma[2], lim = in(lim), ans = 0;
pii t;
for(int i = 1; i <= n; i ++) {
for(int j = i; j <= n; j ++) {
for(int l = 1, r = 1; r <= m;) { // 简单的双指针
while((r - l + 1) * (j - i + 1) <= ans and r <= m + 1) r ++; // 剪枝,不可能比 ans 优就跳过
if(r > m) break;
mi[0] = i, ma[0] = j, mi[1] = l, ma[1] = r;
t = query(rt, mi, ma);
if(t.second - t.first <= lim) {
ans = max(ans, (r - l + 1) * (j - i + 1));
r ++;
}
else {
l ++, r ++;
}
}
}
}
out(ans);
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~