[bzoj4653] [Noi2016] 区间

Description

在数轴上有 \(n\) 个闭区间 \([l1,r1],[l2,r2],...,[ln,rn]\) 。现在要从中选出 \(m\) 个区间,使得这 \(m\) 个区间共同包含至少一个位置。换句话说,就是使得存在一个 \(x\),使得对于每一个被选中的区间 \([li,ri]\),都有 \(li \leq x \leq ri\)
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 \([li,ri]\) 的长度定义为 \(ri−li\),即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 \(−1\)

Input

第一行包含两个正整数 \(n\) ,\(m\) 用空格隔开,意义如上文所述。保证 \(1 \leq m \leq n\)
接下来 \(n\) 行,每行表示一个区间,包含用空格隔开的两个整数 \(li\)\(ri\) 为该区间的左右端点。
\(N \leq 500000,M \leq 200000,0 \leq li \leq ri \leq 10^9\)

Output

只有一行,包含一个正整数,即最小花费。

Sample Input

6 3

3 5

1 2

3 4

2 2

1 5

1 4

Sample Output

2


想法

还是挺巧的(或是说我太弱了)。
一开始我的想法是枚举那 \(m\) 个区间都包含的点 \(x\) ,在包含 \(x\) 的那些区间中找答案,但发现这样并不好做。

于是换一个角度,从答案出发。
在一个合法方案中,设选中的区间最长与最短分别为 \(mx\)\(mn\)
那么如果将长度在 \([mn,mx]\) 中的所有区间都选上,显然是满足条件的。
不妨将区间按长度从小到大排序,每次选连续的一段区间,只要这一段区间将某个点覆盖大于等于 \(m\) 次,就满足条件。

线段树+双指针(尺取法)维护就可以了~


代码

别忘了在不存在方案时输出-1!

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x;
}

const int N = 500005;

struct data{
	int l,r,len;
	bool operator < (const data &b) const { return len<b.len; }
}d[N];
int n,m;
int b[N*2],tot;

struct tree{
	tree *ch[2];
	int mx,lazy;
}pool[N*4],*root;
int cnt;
void build(tree *p,int l,int r){
	p->mx=p->lazy=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(p->ch[0]=&pool[++cnt],l,mid);
	build(p->ch[1]=&pool[++cnt],mid+1,r);
}
void pushdown(tree *p){
	if(!p->lazy) return;
	p->ch[0]->lazy+=p->lazy; p->ch[0]->mx+=p->lazy;
	p->ch[1]->lazy+=p->lazy; p->ch[1]->mx+=p->lazy;
	p->lazy=0;
}
void add(tree *p,int l,int r,int L,int R,int c){
	if(l==L && r==R) { p->lazy+=c; p->mx+=c; return; }
	pushdown(p);
	int mid=(l+r)>>1;
	if(R<=mid) add(p->ch[0],l,mid,L,R,c);
	else if(L>mid) add(p->ch[1],mid+1,r,L,R,c);
	else{
		add(p->ch[0],l,mid,L,mid,c);
		add(p->ch[1],mid+1,r,mid+1,R,c);
	}
	p->mx=max(p->ch[0]->mx,p->ch[1]->mx);
}

int main()
{
	n=read(); m=read();
	for(int i=1;i<=n;i++){
		d[i].l=read(); d[i].r=read(); d[i].len=d[i].r-d[i].l;
		b[++tot]=d[i].l; b[++tot]=d[i].r;
	}
	
	sort(b+1,b+1+tot);
	tot=unique(b+1,b+1+tot)-b-1;
	for(int i=1;i<=n;i++){
		d[i].l=lower_bound(b+1,b+1+tot,d[i].l)-b;
		d[i].r=lower_bound(b+1,b+1+tot,d[i].r)-b;
	}
	sort(d+1,d+1+n);
	
	build(root=&pool[++cnt],1,tot);
	add(root,1,tot,d[1].l,d[1].r,1);
	int l=1,r=1,ans=1000000005;
	for(;l<=n;l++){
		if(l!=1) add(root,1,tot,d[l-1].l,d[l-1].r,-1);
		for(;root->mx<m && r<n;){
			r++;
			add(root,1,tot,d[r].l,d[r].r,1);
		}
		if(root->mx<m) break;
		ans=min(ans,d[r].len-d[l].len);
	}
	if(ans!=1000000005) printf("%d\n",ans);
	else printf("-1\n");
	
	return 0;
}
posted @ 2019-08-14 10:23  秋千旁的蜂蝶~  阅读(127)  评论(0编辑  收藏  举报