[ NOI 2016 ] 区间
\(\\\)
\(Description\)
给出\(N\)个区间,现在要求选出\(M\)个区间,使得这些区间交集不为空,求所有合法的选法里,每一种选法对应的最长区间长度减最短区间长度最小值的多少。
若不存在一组合法的解就输出\(-1\)。
- \(N\le 5\times10^5,m\le 2\times 10^5,l_i,r_i\le 10^9\)
\(\\\)
\(Solution\)
考虑要求区间集合最长和最短的长度之差最小并不好做,但是如果将区间按照长度排序每一个合法集合一定可以由端点所在位置框起来,即答案一定是一个序列的一段的端点之差。
然后考虑右端点为每一个位置的时候左端点最靠右能到哪里。这是一个双指针扫描的过程,我只要一直保证交集不为空即可。这个验证可以用线段树做,新来一个区间就区间 \(+1\),去掉一个前面的区间就区间 \(-1\),最后查询整个数列单点最大是否 \(\ge M\) 就可以了。
有两个细节需要注意:
- 当当前最大值是 \(M\) 的时候依旧要考虑左端点是否能右移,因为可能开头的一段对集合求交并没有贡献。
- 无需考虑除掉区间长度以外的元素顺序对答案的影响。因为等长的区间不管怎么排序贡献是一样的,要删就都能删,不能删只删几个也不会使答案优秀。
\(\\\)
\(Code\)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1000010
#define gc getchar
#define Rg register
#define mid ((l+r)>>1)
#define inf 2000000000
using namespace std;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int n,m,tot,ans,p[N<<1];
struct line{int l,r,len;}l[N];
inline bool cmp(line x,line y){
return x.len==y.len?x.l<y.l:x.len<y.len;
}
struct segment{
int root,ptr;
inline int newnode(){return ++ptr;}
struct node{int ls,rs,mx,tag;}c[N<<2];
void build(int &rt,int l,int r){
rt=newnode();
if(l==r) return;
build(c[rt].ls,l,mid);
build(c[rt].rs,mid+1,r);
}
inline void pushup(int rt){
c[rt].mx=max(c[c[rt].ls].mx,c[c[rt].rs].mx);
}
inline void pushdown(int rt){
if(c[rt].tag){
c[c[rt].ls].tag+=c[rt].tag;
c[c[rt].rs].tag+=c[rt].tag;
c[c[rt].ls].mx+=c[rt].tag;
c[c[rt].rs].mx+=c[rt].tag;
c[rt].tag=0;
}
}
void updata(int rt,int l,int r,int L,int R,int w){
if(r<L||l>R) return;
if(l>=L&&r<=R){
c[rt].tag+=w;
c[rt].mx+=w;
return;
}
pushdown(rt);
if(L<=mid) updata(c[rt].ls,l,mid,L,R,w);
if(R>=mid+1) updata(c[rt].rs,mid+1,r,L,R,w);
pushup(rt);
}
}seg;
int main(){
n=rd(); m=rd();
for(Rg int i=1;i<=n;++i){
p[i]=(l[i].l=rd());
p[i+n]=(l[i].r=rd());
l[i].len=l[i].r-l[i].l;
}
sort(l+1,l+1+n,cmp);
sort(p+1,p+1+(n<<1));
tot=1;
for(Rg int i=2;i<=(n<<1);++i) if(p[i]!=p[i-1]) p[++tot]=p[i];
for(Rg int i=1;i<=n;++i){
l[i].l=lower_bound(p+1,p+1+tot,l[i].l)-p;
l[i].r=lower_bound(p+1,p+1+tot,l[i].r)-p;
}
seg.build(seg.root,1,tot);
for(Rg int i=1;i<=m;++i)
seg.updata(seg.root,1,tot,l[i].l,l[i].r,1);
ans=(seg.c[seg.root].mx==m)?l[m].len-l[1].len:inf;
for(Rg int i=m+1,j=1;i<=n;++i){
seg.updata(seg.root,1,tot,l[i].l,l[i].r,1);
if(seg.c[seg.root].mx>=m){
while(seg.c[seg.root].mx>=m){
seg.updata(seg.root,1,tot,l[j].l,l[j].r,-1);
++j;
}
--j;
seg.updata(seg.root,1,tot,l[j].l,l[j].r,1);
ans=min(ans,l[i].len-l[j].len);
}
}
printf("%d\n",ans==inf?-1:ans);
return 0;
}