[BZOJ2687]交与并

在 UNR 上看到这样一道题,当时想起来就是大视野原题,发现自己竟然没过,就刷了一波。

[BZOJ2687]交与并

试题描述

对于一个区间集合 ({A_1,A_2,cdots,A_K}(K>1, forall i e j, A_i e A_j)),我们定义其权值 (W=|A_1 cup A_2 cup cdots cup A_K| cdot |A_1 cap A_2 cap cdots cap A_K|) 当然,如果这些区间没有交集则权值为 (0)

输入

给你 (N) 个((1<N le 10^6))各不相同的区间,请你从中找出若干个区间使其权值最大。

第一行 (N),接下来(N)(l r(1 le l<r le 10^6))

输出

最大权值。

输入示例

4
1 6
4 8
2 7
3 5

输出示例

24

数据规模及约定

见“输入

题解

显然只有两个区间有意义。因为首先可以把被包含的区间去掉,然后按照左端点排序,那么答案等于我选择的所有区间中最靠左的和最靠右的答案。

假设两个区间 (i)(j)(假设 (j)(i) 左,且两区间有交),答案就是 ((r_i - l_j)(r_j - l_i) = r_i r_j - l_i r_i - l_j r_j + l_i l_j),读者不妨自己证明这个 (j ightarrow i) 随着 (i) 往右,最优的 (j) 满足决策单调性。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)) { if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 1000010
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
#define LL long long

int n, N;
pii _lr[maxn], lr[maxn];

LL calc(pii a, pii b) { return (LL)(max(a.y, b.y) - min(a.x, b.x)) * (min(a.y, b.y) - max(a.x, b.x)); }
const bool cmp(const pii &a, const pii &b) { return a.x != b.x ? a.x < b.x : a.y > b.y; }
const bool wrap(const pii &a, const pii &b) { return (a.x <= b.x && b.y < a.y) || (a.x < b.x && b.y <= a.y); }

struct Info {
	int l, r, p;
	Info(int _1 = 0, int _2 = 0, int _3 = 0): l(_1), r(_2), p(_3) {}
} S[maxn];
int hd, top;
LL ans;

int main() {
	n = read();
	rep(i, 1, n) _lr[i].x = read(), _lr[i].y = read();
	
	sort(_lr + 1, _lr + n + 1, cmp);
	lr[++N] = _lr[1];
	rep(i, 2, n) if(_lr[i].y > lr[N].y) lr[++N] = _lr[i]; else ans = max(ans, calc(lr[N], _lr[i]));
	S[hd = top = 1] = Info(2, N, 1);
	rep(i, 2, N) {
		while(hd < top && i > S[hd].r) hd++;
		ans = max(ans, calc(lr[S[hd].p], lr[i]));
		while(top > hd && calc(lr[i], lr[S[top].l]) >= calc(lr[S[top].p], lr[S[top].l])) top--;
		int l = i + 1, r = N;
		while(l < r) {
			int mid = l + r >> 1;
			if(calc(lr[S[top].p], lr[mid]) > calc(lr[i], lr[mid])) l = mid + 1; else r = mid;
		}
		if(calc(lr[S[top].p], lr[l]) > calc(lr[i], lr[l])) continue;
		S[top+1] = Info(l, N, i);
		S[top].r = l - 1; ++top;
	}
	
	printf("%lld
", ans);
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9314271.html