LOJ#535. 「LibreOJ Round #6」花火(最大子矩形问题+扫描线+线段树)
题解:
有点感觉这个是个经典题,我咋想不到呢。
不交换就是逆序对数。
考虑交换\(h[x]\)和\(h[y]\)。
得满足\(h[x]>h[y]\),不然没有意义。
那么逆序对会减少\(2*\sum_{x<z<y}h[x]>h[z]>h[y]\)
这就相当于矩形数点问题:以\((x,h[x])\)为左上角,\((y,h[y])\)为右下角的矩形内的点数。
注意到对一个左上角\((x,h[x])\)如果有\((u,h[u])\)满足\(u<x且h[u]>h[x]\)
即\((u,h[u])\)在\((x,h[x])\)的左上角,既然要使矩形覆盖点数最多,那么选\((u,h[u])\)作为左上角一定优于\((x,h[x])\)。
对右下角同理。
可能的左上角点满足\(x\)递增时,\(h[x]\)下降。
现在再考虑每个点\(z\),在\(z\)的左上角的点就是一个区间,右下角同理,这个区间可以二分出来。
那么变成了矩形加,求最大值问题,扫描线+线段树即可。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 3e5 + 5;
int n, h[N], p[N], q[N];
struct P {
int x, y, f;
};
vector<P> d[N];
#define pb push_back
void Init() {
scanf("%d", &n);
fo(i, 1, n) scanf("%d", &h[i]);
fo(i, 1, n) p[i] = max(p[i - 1], h[i]);
q[n + 1] = 3e5 + 1; fd(i, n, 1) q[i] = min(q[i + 1], h[i]);
fo(i, 1, n) {
int x = 0, y = n + 1;
for(int l = 1, r = i - 1; l <= r; ) {
int m = l + r >> 1;
if(p[m] < h[i]) x = m, l = m + 1; else r = m - 1;
}
for(int l = i + 1, r = n; l <= r; ) {
int m = l + r >> 1;
if(q[m] > h[i]) y = m, r = m - 1; else l = m + 1;
}
if(x + 1 <= i - 1 && i + 1 <= y - 1) {
d[x + 1].pb((P) {i + 1, y - 1, 1});
d[i].pb((P) {i + 1, y - 1, -1});
}
}
}
int t[N * 4], lz[N * 4];
int pl, pr, px;
#define i0 i + i
#define i1 i + i + 1
void pla(int i, int y) { t[i] += y, lz[i] += y;}
void down(int i) { if(lz[i]) pla(i0, lz[i]), pla(i1, lz[i]), lz[i] = 0;}
void add(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
pla(i, px); return;
}
int m = x + y >> 1; down(i);
add(i0, x, m); add(i1, m + 1, y);
t[i] = max(t[i0], t[i1]);
}
#define low(x) ((x) & -(x))
ll ans, f[N];
void add(int x, int y) {
for(; x <= 3e5; x += low(x)) f[x] += y;
}
ll sum(int x) {
ll s = 0;
for(; x; x -= low(x)) s += f[x];
return s;
}
void Work() {
fo(i, 1, n) {
ff(j, 0, d[i].size()) {
P v = d[i][j];
pl = v.x, pr = v.y, px = v.f;
add(1, 1, n);
}
ans = max(ans, (ll) t[1]);
}
ans = -2 * ans;
fo(i, 1, n) {
ans += sum(3e5) - sum(h[i]);
add(h[i], 1);
}
pp("%lld\n", ans);
}
int main() {
Init();
Work();
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址