[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;
}