[NOI2016]区间

先把所有区间按长度从小到大排序,我们选择一些连续的区间就可以得到最优解(最优解加上几个多余的区间就是排好序之后连续的区间)

枚举左右端点,用线段树维护区间加和询问$\max$可以做到$O\left(n^2\log_2n\right)$

但实际上我们只需要从小到大枚举右端点,此时左端点的选择是单调不降的,因为如果当前选择的区间已经满足要求,把左端点往左挪不会使答案变小,时间复杂度$O(n\log_2n)$

#include<stdio.h>
#include<algorithm>
using namespace std;
const int inf=2147483647;
int mx[4000010],d[4000010];
void pushup(int x){mx[x]=max(mx[x<<1],mx[x<<1|1]);}
void ad(int x,int v){
	d[x]+=v;
	mx[x]+=v;
}
void pushdown(int x){
	if(d[x]){
		ad(x<<1,d[x]);
		ad(x<<1|1,d[x]);
		d[x]=0;
	}
}
void modify(int L,int R,int v,int l,int r,int x){
	if(L<=l&&r<=R)return ad(x,v);
	pushdown(x);
	int mid=(l+r)>>1;
	if(L<=mid)modify(L,R,v,l,mid,x<<1);
	if(mid<R)modify(L,R,v,mid+1,r,x<<1|1);
	pushup(x);
}
struct seg{
	int l,r,d;
}p[500010];
bool operator<(seg a,seg b){return a.d<b.d;}
struct st{
	int v,i,f;
	st(int a=0,int b=0,int c=0){v=a;i=b;f=c;}
}s[1000010];
bool operator<(st a,st b){return a.v<b.v;}
int main(){
	int n,m,i,N,l,ans;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d%d",&p[i].l,&p[i].r);
		p[i].d=p[i].r-p[i].l;
		s[i*2-1]=st(p[i].l,i,0);
		s[i*2]=st(p[i].r,i,1);
	}
	sort(s+1,s+2*n+1);
	s[0].v=-1;
	N=0;
	for(i=1;i<=n*2;i++){
		if(s[i].v!=s[i-1].v)N++;
		(s[i].f?p[s[i].i].r:p[s[i].i].l)=N;
	}
	sort(p+1,p+n+1);
	l=1;
	ans=inf;
	for(i=1;i<=n;i++){
		modify(p[i].l,p[i].r,1,1,N,1);
		while(mx[1]>=m){
			ans=min(ans,p[i].d-p[l].d);
			modify(p[l].l,p[l].r,-1,1,N,1);
			l++;
		}
	}
	if(ans==inf)ans=-1;
	printf("%d",ans);
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9240188.html
今日推荐