BZOJ2687: 交与并
$n \leq 1e6$个区间,定义一个区间集合的权值为:并集大小-交集大小。求一个权值最大的大小至少为2的集合。
好题。
在一个区间集合中,可以发现除了左右端点涉及的区间外,里面剩余的区间越少,并集不会变但交集会越大,因此答案会更优。但我们需要集合大小至少为2,因此变成选两个区间。
左右端点可能涉及一个或两个区间。前者要求选中两个包含关系的集合,后者则不包含。
包含:
按右端点排序后,对每个$i$,求$L_j \geq L_i$的最大区间,$j<i$。
其实不必这样。当两个区间成包含关系时,小区间和其他区间不可能比大区间和其他间更优(画画图分分类),因此小区间贡献完一次就直接删掉。可以用个栈。
不包含:
前面说被包含的区间在这没贡献了,删之,得到一个$R$递增$L$递增的区间序列。排序之。
然后可以证明决策单调性。我:???
好这么证。无非就是要证:$p<q<i$时决策$i$,$p$比$q$优,那么在决策$i-1$时$p$不可能不如$q$。
反证之。由$p$比$q$优得到$(R_i-L_p)(R_p-L_i)>(R_i-L_q)(R_q-L_i)$,整理得$R_i(R_p-R_q)+L_i(L_p-L_q)-L_pR_p+L_qR_q>0$。在$i-1$处由$q$比$p$优同理可以得到$R_{i-1}(R_q-R_p)+L_{i-1}(L_q-L_p)+L_pR_p-L_qR_q>0$,两式相减得$(R_i-R_{i-1})(R_p-R_q)+(L_i-L_{i-1})(L_p-L_q)>0$,但这是不可能的,得证。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 //#include<set> 5 #include<algorithm> 6 //#include<math.h> 7 //#include<iostream> 8 //#include<time.h> 9 using namespace std; 10 11 #define LL long long 12 int qread() 13 { 14 char c; int s=0,t=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (t=-1); 15 do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*t; 16 } 17 18 //Pay attention to read! 19 20 int n; 21 #define maxn 1000011 22 struct QQ{int l,r,v;}qq[maxn]; 23 bool vis[maxn]; 24 bool cmpr(const QQ &a,const QQ &b) {return a.r<b.r;} 25 LL ans; 26 void solve(int L,int R,int ql,int qr) 27 { 28 if (L>R || ql>qr) return; 29 int mid=(ql+qr)>>1; 30 int id=mid; LL tmp=0,v; 31 for (int i=L,to=min(mid-1,R);i<=to;i++) 32 if ((v=(qq[mid].r-qq[i].l)*1ll*(qq[i].r-qq[mid].l))>tmp) {tmp=v; id=i;} 33 ans=max(ans,tmp); 34 solve(L,id,ql,mid-1); solve(id,R,mid+1,qr); 35 } 36 37 int sta[maxn],top=0; 38 int main() 39 { 40 n=qread(); 41 for (int i=1;i<=n;i++) {qq[i].l=qread(); qq[i].r=qread(); qq[i].v=qq[i].r-qq[i].l;} 42 sort(qq+1,qq+1+n,cmpr); 43 44 ans=0; 45 sta[++top]=1; 46 for (int i=2;i<=n;i++) 47 { 48 int last=0; 49 while (top && qq[sta[top]].l>=qq[i].l) 50 last=sta[top],vis[last]=1,ans=max(ans,qq[i].v*1ll*qq[last].v),top--; 51 sta[++top]=i; 52 } 53 54 int nn=0; 55 for (int i=1;i<=n;i++) if (!vis[i]) qq[++nn]=qq[i]; n=nn; 56 solve(1,n,1,n); 57 58 printf("%lld\n",ans); 59 return 0; 60 }