AGC040 B Two Contests 最大最小傻傻分不清楚~

题目链接

等价命题:

给定一些整数区间。现在要把这些区间分割成两个分割集合A和B,然后求 max ⁡ A , B ∣ ( ⋂ r ∈ A r ) ∪ ( ⋂ r ∈ B r ) ∣ \max_{A,B}{\left |(\bigcap_{r\in A}r)\cup(\bigcap_{r\in B}r)\right |} A,Bmax(rAr)(rBr)
先找出区间左端点最大的区间s和右端点最小的区间t
(1)若s,t同属于一个分割集合,那么该分割集合不管再加什么,其交集出来的结果都跟仅含有s,t的区间长度相同。所以这时这个集合除了放s,t外,可以任意放其他区间都不会改变这个分割集合的最终区间长度。那么另一个分割集合显然取所有区间最大的放入,即可算出当前情况下的最大值。 O ( n ) O(n) O(n)
(2)若s,t分属两个分割集合,设s所在分割集合为A,t所在分割集合为B,那么A中的任意区间的右端点到s的左端点为有效区间,设
a i = max ⁡ ( r i − l ( s ) + 1 , 0 ) a_i=\max (r_i-l(s)+1,0) ai=max(ril(s)+1,0)那么A中所有区间的交集的区间大小就是A中所有区间所对应 a i a_i ai最小值。
同理可设
b i = max ⁡ ( r ( t ) − l i + 1 , 0 ) b_i=\max (r(t)-l_i+1,0) bi=max(r(t)li+1,0)
表示B中各个区间的有效个数。B中所有区间的交集的区间大小就是B中所有区间所对应 b i b_i bi最小值。
总结来看就是求哪个分割下,两个集合的 a i a_i ai的最小值和 b i b_i bi的最小值的最大。
如果一个一个枚举就变成了 O ( n 2 ) O(n^2) O(n2)来不及,这里我的做法是把 a i a_i ai b i b_i bi做成pair,然后全部塞进vector里面,对vector从小到大排序后,开始依次遍历vector:记录 b i b_i bi的最小值,同时 a i a_i ai是排好序的,所以能确定当前 a i a_i ai i i i之后所有 a i a_i ai的最小值,很自然的,我们可以把 i i i之后的作为分割集合A,因为最小值就是 a i a_i ai那么 i − 1 i-1 i1及这之前的看做分割集合B,又因为 b i b_i bi的最小值我们一直在记录,所以每次循环能立刻取到。排序复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),最后的枚举为 O ( n ) O(n) O(n),整体压缩在 O ( n log ⁡ n ) O(n\log n) O(nlogn)

我提交的源代码

下面是cpp风格的伪代码:

#include<bits/stdc++.h>
using namespace std;
//省略一些宏
//---------------------
#define MAXN 100005
#define INF 1000000007
//---------------------
ll n;
ll l[MAXN],r[MAXN];
vector<pll> arr;
 
int main(){
 
	cin >> n;
	REP(i,n) cin >> l[i] >> r[i];
 
	ll res = 0;
 
	ll lmax = 0;
	ll rmin = INF;
	ll nmax = 0;
	REP(i,n) {
		lmax = max(lmax,l[i]); 
		rmin = min(rmin,r[i]); 
		nmax = max(nmax,r[i]-l[i]+1);
	}
 
	res = max(rmin-lmax+1,0ll) + nmax;
 
	REP(i,n){
		arr.PB(pll(max(r[i]-lmax+1,0ll),max(rmin-l[i]+1,0ll)));
	}
 
	sort(ALL(arr));
 
	ll minb = INF;
	REP(i,n-1){
		ll a = arr[i+1].P1, b = arr[i].P2;
		minb = min(minb,b);
		res = max(res,a+minb);
	}
 
	PRT(res);
 
	return 0;
}

反思

上述思路由题解提供。一开始一直想用dp,滑动窗口,二分查找之类的骚操作,结果是带有分类讨论的贪心算法和前缀和,深刻感觉自己能力不足。之后遇到这样类似的思维题要善于简化题目和思维回路,多动笔找一找等价的题目表达为上策。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值