LOJ#535. 「LibreOJ Round #6」花火(最大子矩形问题+扫描线+线段树)

https://loj.ac/problem/535

题解:

有点感觉这个是个经典题,我咋想不到呢。

不交换就是逆序对数。

考虑交换(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("
")
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
", ans);
}

int main() {
	Init();
	Work();
}

原文地址:https://www.cnblogs.com/coldchair/p/12362067.html