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