题目链接
题意:
在数轴上有
N
N
N个闭区间
[
l
1
,
r
1
]
,
[
l
2
,
r
2
]
,
.
.
.
,
[
l
n
,
r
n
]
[l_1,r_1],[l_2,r_2],...,[l_n,r_n]
[l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出
M
M
M个区间,使得这
M
M
M个区间共同包含至少一个位置。换句话说,就是使得存在一个
x
x
x,使得对于每一个被选中的区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri],都有
l
i
≤
x
≤
r
i
l_i≤x≤r_i
li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]的长度定义为
r
i
−
l
i
r_i-l_i
ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出-1 。
N
<
=
5
e
5
,
M
<
=
2
e
5
,
0
<
=
l
i
<
=
r
i
<
=
1
e
9
N<=5e5,M<=2e5,0<=l_i<=r_i<=1e9
N<=5e5,M<=2e5,0<=li<=ri<=1e9
题解:
update:
之前题解说的乱七八糟,重新施工了 ——2020.10.15
我们先对所有的 l l l和 r r r离散化,对每一个区间按照离散化之前区间长度为关键字从小到大排序,我们答案要我们插入的所有区间中最大的和最小的尽可能接近,所有我们从小到大插入区间,看插入到什么时候出现一个点被覆盖了 m m m次。
每当插入一个区间,我们就在线段树上进行一次区间加一,然后求全局最大值,看是否是 m m m。如果是 m m m我们就依次从小到大删除区间,也就是在要删除的区间上做区间减一操作,做直到最大值不是 m m m,这时删除的最后一个区间的长度就是当前这个区间加进来后能找到的符合要求的长度最大的小区间,于是用这个区间的长度和当前枚举到的区间的长度更新答案。
考虑为什么把之前的区间删掉不会影响最终答案。也是比较显然的,我们是按照区间长度依次加入区间的,就算和之前的那些区间也可以构成符合条件的 m m m个区间,但是由于最大区间比之前大,最小区间只可能比当前能用来更新的区间小,所以两者之差一定比当前答案大,不可能成为最终结果。
于是我们就做完了。复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,b[1000010],cnt,l,r,ans=2e9;
struct node
{
int l,r,del;
}a[500010];
struct tree
{
int l,r,tag,mx;
}tr[4000010];
inline int cmp(node x,node y)
{
return x.del<y.del;
}
inline void pushup(int rt)
{
tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx);
}
inline void build(int rt,int l,int r)
{
tr[rt].l=l;
tr[rt].r=r;
tr[rt].mx=0;
tr[rt].tag=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
inline void pushdown(int rt)
{
if(tr[rt].tag!=0)
{
tr[rt<<1].mx+=tr[rt].tag;
tr[rt<<1|1].mx+=tr[rt].tag;
tr[rt<<1].tag+=tr[rt].tag;
tr[rt<<1|1].tag+=tr[rt].tag;
tr[rt].tag=0;
}
}
inline void update(int rt,int le,int ri,int x)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
{
tr[rt].mx+=x;
tr[rt].tag+=x;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(le<=mid)
update(rt<<1,le,ri,x);
if(mid+1<=ri)
update(rt<<1|1,le,ri,x);
pushup(rt);
}
inline int query(int rt,int le,int ri)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
return tr[rt].mx;
pushdown(rt);
int mid=(l+r)>>1,res=0;
if(le<=mid)
res=max(res,query(rt<<1,le,ri));
if(mid+1<=ri)
res=max(res,query(rt<<1|1,le,ri));
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&a[i].l,&a[i].r);
b[++cnt]=a[i].l;
b[++cnt]=a[i].r;
a[i].del=a[i].r-a[i].l;
}
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-b-1;
for(int i=1;i<=n;++i)
{
a[i].l=lower_bound(b+1,b+cnt+1,a[i].l)-b;
a[i].r=lower_bound(b+1,b+cnt+1,a[i].r)-b;
}
sort(a+1,a+n+1,cmp);
build(1,1,cnt);
l=1;
r=1;
while(l<=n&&r<=n)
{
while(r<=n&&query(1,1,cnt)<m)
{
update(1,a[r].l,a[r].r,1);
++r;
}
if(query(1,1,cnt)<m||r>n)
break;
while(query(1,1,cnt)>=m&&l<=n)
{
update(1,a[l].l,a[l].r,-1);
++l;
}
ans=min(ans,a[r-1].del-a[l-1].del);
}
if(ans<2e9)
printf("%d\n",ans);
else
printf("-1\n");
return 0;
}