P1712 [NOI2016]区间 尺取法 +线段树

我们需要枚举区间 看是否有一个点被覆盖m次  暴力的枚举肯定是不行的  那么可以采用尺取法

首先根据长度对n个区间进行升序排序  假设当前枚举的为 标号L~R的区间  我们用线段树维护最大值 看是否有一个位置被覆盖了m次  如果有 更新答案  接着我们压缩L~R区间  (因为我们要最小值 当L越大 差值越小) 也就是在 有一个位置被覆盖次数>=m时让L最大   更新完这个答案后 在向后扩展R就行了 

尺取法的一般步骤是:

1.初始时 L=R=0

2.R向右扩展知道满足条件   

3.在第二步的基础上 L向右扩展 并保证满足条件 

4.此时得到了 一个以R为右端点的最小可行区间 更新答案 

5.重复上述过程 

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+100;
int has[N*2],tot;
int mx[N<<4],lazy[N<<4];
struct node{
	int l,r,len;
	bool operator < (const node &a){
		return len<a.len;
	} 
}p[N];
int get(int x){
	return lower_bound(has+1,has+1+tot,x)-has;
}
void pushup(int id){
	mx[id]=max(mx[id<<1],mx[id<<1|1]);
}
void pushdown(int id){
	if(lazy[id]){
		lazy[id<<1]+=lazy[id];
		lazy[id<<1|1]+=lazy[id];
		mx[id<<1]+=lazy[id];
		mx[id<<1|1]+=lazy[id];
		lazy[id]=0;
		return;
	}
}
void update(int id,int l,int r,int L,int R,int v){
	if(L<=l&&R>=r){
		mx[id]+=v;
		lazy[id]+=v;
		return;
	}
	int mid = l+r>>1;
	pushdown(id);
	if(L<=mid) update(id<<1,l,mid,L,R,v);
	if(R>mid) update(id<<1|1,mid+1,r,L,R,v);
	pushup(id);
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++) 
	scanf("%d%d",&p[i].l,&p[i].r),p[i].len=p[i].r-p[i].l,has[++tot]=p[i].l,has[++tot]=p[i].r;
	sort(p+1,p+1+n);
	sort(has+1,has+1+tot);
	tot=unique(has+1,has+1+tot)-has;
	for(int i = 1; i <= n; i++)	
	p[i].l=get(p[i].l),p[i].r=get(p[i].r);
	int L=1,ans=2e9;
	for(int i = 1; i <= n; i++){
		update(1,1,tot,p[i].l,p[i].r,1);
		while(mx[1]>=m&&L<=i){
			ans=min(ans,p[i].len-p[L].len);
			update(1,1,tot,p[L].l,p[L].r,-1);
			L++;
		}				
	}
	printf("%d\n",ans==2e9?-1:ans);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值