洛谷1712 BZOJ4653 NOI2016 区间 线段树 离散化

题目链接
题意:
在数轴上有 N N N个闭区间 [ l 1 , r 1 ] , [ l 2 , r 2 ] , . . . , [ l n , r n ] [l_1,r_1],[l_2,r_2],...,[l_n,r_n] [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 M M M个区间,使得这 M M M个区间共同包含至少一个位置。换句话说,就是使得存在一个 x x x,使得对于每一个被选中的区间 [ l i , r i ] [l_i,r_i] [li,ri],都有 l i ≤ x ≤ r i l_i≤x≤r_i lixri
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [ l i , r i ] [l_i,r_i] [li,ri]的长度定义为 r i − l i r_i-l_i rili,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出-1 。
N < = 5 e 5 , M < = 2 e 5 , 0 < = l i < = r i < = 1 e 9 N<=5e5,M<=2e5,0<=l_i<=r_i<=1e9 N<=5e5,M<=2e5,0<=li<=ri<=1e9

题解:
update:
之前题解说的乱七八糟,重新施工了 ——2020.10.15

我们先对所有的 l l l r r r离散化,对每一个区间按照离散化之前区间长度为关键字从小到大排序,我们答案要我们插入的所有区间中最大的和最小的尽可能接近,所有我们从小到大插入区间,看插入到什么时候出现一个点被覆盖了 m m m次。

每当插入一个区间,我们就在线段树上进行一次区间加一,然后求全局最大值,看是否是 m m m。如果是 m m m我们就依次从小到大删除区间,也就是在要删除的区间上做区间减一操作,做直到最大值不是 m m m,这时删除的最后一个区间的长度就是当前这个区间加进来后能找到的符合要求的长度最大的小区间,于是用这个区间的长度和当前枚举到的区间的长度更新答案。

考虑为什么把之前的区间删掉不会影响最终答案。也是比较显然的,我们是按照区间长度依次加入区间的,就算和之前的那些区间也可以构成符合条件的 m m m个区间,但是由于最大区间比之前大,最小区间只可能比当前能用来更新的区间小,所以两者之差一定比当前答案大,不可能成为最终结果。

于是我们就做完了。复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,b[1000010],cnt,l,r,ans=2e9;
struct node
{
	int l,r,del;
}a[500010];
struct tree
{
	int l,r,tag,mx;
}tr[4000010];
inline int cmp(node x,node y)
{
	return x.del<y.del;
}
inline void pushup(int rt)
{
	tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx);
}
inline void build(int rt,int l,int r)
{
	tr[rt].l=l;
	tr[rt].r=r;
	tr[rt].mx=0;
	tr[rt].tag=0;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
}
inline void pushdown(int rt)
{
	if(tr[rt].tag!=0)
	{
		tr[rt<<1].mx+=tr[rt].tag;
		tr[rt<<1|1].mx+=tr[rt].tag;
		tr[rt<<1].tag+=tr[rt].tag;
		tr[rt<<1|1].tag+=tr[rt].tag;
		tr[rt].tag=0;
	}
}
inline void update(int rt,int le,int ri,int x)
{
	int l=tr[rt].l,r=tr[rt].r;
	if(le<=l&&r<=ri)
	{
		tr[rt].mx+=x;
		tr[rt].tag+=x;
		return;
	}
	pushdown(rt);
	int mid=(l+r)>>1;
	if(le<=mid)
	update(rt<<1,le,ri,x);
	if(mid+1<=ri)
	update(rt<<1|1,le,ri,x);
	pushup(rt);
}
inline int query(int rt,int le,int ri)
{
	int l=tr[rt].l,r=tr[rt].r;
	if(le<=l&&r<=ri)
	return tr[rt].mx;
	pushdown(rt);
	int mid=(l+r)>>1,res=0;
	if(le<=mid)
	res=max(res,query(rt<<1,le,ri));
	if(mid+1<=ri)
	res=max(res,query(rt<<1|1,le,ri));
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		b[++cnt]=a[i].l;
		b[++cnt]=a[i].r;
		a[i].del=a[i].r-a[i].l;
	}
	sort(b+1,b+cnt+1);
	cnt=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=n;++i)
	{
		a[i].l=lower_bound(b+1,b+cnt+1,a[i].l)-b;
		a[i].r=lower_bound(b+1,b+cnt+1,a[i].r)-b;
	}
	sort(a+1,a+n+1,cmp);
	build(1,1,cnt);
	l=1;
	r=1;
	while(l<=n&&r<=n)
	{		
		while(r<=n&&query(1,1,cnt)<m)
		{			
			update(1,a[r].l,a[r].r,1);
			++r;						
		}
		if(query(1,1,cnt)<m||r>n)
		break;
		while(query(1,1,cnt)>=m&&l<=n)
		{	
								
			update(1,a[l].l,a[l].r,-1);
			++l;			
		}	
		ans=min(ans,a[r-1].del-a[l-1].del);			
	}	
	if(ans<2e9)
	printf("%d\n",ans);
	else
	printf("-1\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值