题目描述
在数轴上有NN 个闭区间[li,ri] 。现在要从中选出M 个区间,使得这M 个区间共同包含至少一个位置。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间[l,r]的长度定义为r-l,即等于它的右端点的值减去左端点的值。求所有合法方案中最小的花费。如果不存在合法的方案,输出-1 。
输入格式:
第一行包含两个正整数N,MN,M 用空格隔开,意义如上文所述。保证1≤M≤N1≤M≤N
接下来NN 行,每行表示一个区间
N<=500000,M<=200000,0≤li≤ri≤10^9N<=500000,M<=200000,0≤li≤ri≤10
9
输出格式:
只有一行,包含一个正整数,即最小花费。
这道题区间范围那么大,首先肯定要离散化,感觉还要按长度排个序…
一开始的想法是枚举l,用一颗线段树做区间+1,直到最大值等于m停止。
但是这样做是n^2logn的…
仔细观察一下发现随着l的右移r也是单调的。
所以就是尺取法啦。
复杂度O(nlogn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#define N 500010
#define M 200010
using namespace std;
struct node{
int l,r,len;
}b[N];
int Max[8*N],add[8*N],a[2*N];
map <int,int> mp;
bool cmp(node a,node b)
{
return a.len<b.len;
}
void push_up(int root)
{
Max[root]=max(Max[root<<1],Max[root<<1|1]);
}
void push_down(int root)
{
Max[root<<1]+=add[root];
Max[root<<1|1]+=add[root];
add[root<<1]+=add[root];
add[root<<1|1]+=add[root];
add[root]=0;
}
void update(int root,int l,int r,int x,int y,int w)
{
if(x<=l&&y>=r)
{
add[root]+=w;
Max[root]+=w;
return;
}
int mid=(l+r)>>1;
push_down(root);
if(x<=mid) update(root<<1,l,mid,x,y,w);
if(y>mid) update(root<<1|1,mid+1,r,x,y,w);
push_up(root);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&b[i].l,&b[i].r);
a[i]=b[i].l,a[i+n]=b[i].r;
b[i].len=b[i].r-b[i].l;
}
sort(a+1,a+2*n+1);
for(int i=1;i<=2*n;i++) mp[a[i]]=i;
for(int i=1;i<=n;i++) b[i].l=mp[b[i].l],b[i].r=mp[b[i].r];
sort(b+1,b+n+1,cmp);
int j=1;
update(1,1,2*n,b[1].l,b[1].r,1);
int ans=1e9;
for(int i=1;i<=n;i++)
{
while(j<n&&Max[1]<m)
{
j++;
update(1,1,2*n,b[j].l,b[j].r,1);
}
if(Max[1]<m) break;
ans=min(ans,b[j].len-b[i].len);
update(1,1,2*n,b[i].l,b[i].r,-1);
}
printf("%d\n",ans==1e9?-1:ans);
return 0;
}