[决策单调性] 交与并

· · 个人记录

小成从 bzoj 搬来的,题意:对于一个区间集合{A1,A2,⋯,AK}(K>1,∀i≠j,Ai≠Aj),我们定义其权值W=|A1∪A2∪\dots∪AK|*|A1∩A2∩\dots∩AK| 当然,如果这些区间没有交集则权值为 0

首先按 l 排 并去重,容易发现新的数组上,选取的集合必然是连续一段区间,否则交集为 0 没有意义。

假如选择 a_x,a_{x+1}\dots a_y,那么价值为 w(x,y)=(r_y-l_x)(r_x-l_y)

考虑决策点 $p<x$,必然满足 $l_p< l_x< l_y$,那么: $w(x,y)=r_xr_y-l_yr_y-l_xr_x+l_xl_y$。 $w(p,y)=r_p r_y-l_yr_y-l_pr_p+l_pl_y$。 当 $y$ 增长,设其相比于 $l_y$ 增长 $\Delta l$,同理有 $\Delta r$,那么欲证 $w_x$ 的变化量更大: $$\Delta w_x\ge \Delta w_p$$ $$r_x\Delta r+l_x\Delta l\ge r_p\Delta r+l_p\Delta l$$ 注意,$l_yr_y$ 这一项只与 $y$ 有关,比较时舍去即可。 $$\Delta r(r_x-r_p)\ge \Delta l(l_p-l_x)$$ $$\frac {\Delta r} {\Delta l}\ge \frac {l_p-l_x}{r_x-r_p}$$ 显然,$\Delta r,\Delta l> 0$,$l_p<l_x,r_p<r_x$。 所以 $\frac {\Delta r}{\Delta l} >0$,$\frac {l_p-l_x}{r_x-r_p} <0$。 所以不等式成立,证毕。 最后有个坑点,要计算被包含的区间,显然只涉及两个区间,我们可以在去重时计算: 1. 对于极大区间:维护单调栈,满足栈底到栈顶,区间左端点递增、大小递减,每有一段极大区间,就比较栈顶是否小于它,满足则栈顶一定劣于当前区间。 2. 对于小区间:考虑单调栈内覆盖它的区间,显然,栈顶到栈底右端点递减,覆盖它的区间集合必然是栈顶往下连续一段的,二分出最下面的满足条件的区间即可 (貌似可以不二分,从栈底开始检查是否覆盖,若不满足就直接退掉。因为让它再覆盖后面的更小区间,一定不优?不太清楚。) 总复杂度 $O(n\log n)$。 #### 代码 后话:百万搬来的数据出锅了,不太清楚代码写的有无瑕疵,但思路应该没错。 ```cpp #include<bits/stdc++.h> #pragma GCC optimize(2) using namespace std; #define int long long const int N = 1e6 + 5, py = 1e8; int n, _n, ans, opt; int q[N], l, r; struct node{int l, r;} a[N], b[N]; inline bool cmp(node A, node B) {return A.l == B.l ? A.r > B.r : A.l < B.l;} inline int W(int x, int y) {return (b[y].r - b[x].l) * (b[x].r - b[y].l);} inline void calc(int l, int r, int kl, int kr){ if(l > r || kl > kr) return ; int mid = ((l + r) >> 1), p = kl; for(int i = kl; i < min(mid, kr); ++i) if(W(i, mid) >= W(p, mid)) p = i; if(p < mid) ans = max(ans, W(p, mid)); calc(l, mid - 1, kl, p), calc(mid + 1, r, p, kr); } inline int read() {int x = 0, w = 0; char ch = 0; while (!isdigit(ch)) {w |= ch == '-'; ch = getchar();} while (isdigit(ch)) {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return w ? -x : x;} inline int erf(int p){ int L = l - 1, R = r, mid; while(L + 1 < R){ mid = ((L + R) >> 1); if(a[q[mid]].r >= p) R = mid; else L = mid; } return R; } signed main(){ cin >> n; for(int i = 1; i <= n; ++i) a[i].l = read(), a[i].r = read(); sort(a + 1, a + 1 + n, cmp); l = 1, r = 0; for(int i = 1; i <= n; ++i){ if(a[i].r > b[_n].r){ while(l <= r && a[q[r]].r - a[q[r]].l <= a[i].r - a[i].l) --r; q[++r] = i; b[++_n] = a[i]; } else if(l <= r){ int pos = erf(a[i].r); if(a[q[pos]].r >= a[i].r) ans = max(ans, (a[q[pos]].r - a[q[pos]].l) * (a[i].r - a[i].l)); } } n = _n; calc(1, n, 1, n); cout << ans; return 0; } ```