题目分析:
首先思考一个二分答案的做法。我们可以注意到答案具有单调性,所以可以二分答案。
假设当前二分的答案是$ k $。那么按照大小顺序插入每个区间,同时在末端删除会对答案产生影响的区间。这里不妨用线段树维护。这个做法在外国好像叫做two pointers。
如果某个时刻,线段树中有点大于等于$ m $,说明这个答案是合理的,可以向上二分。若全程没有点大于$ m $说明这个答案不合理,向下二分。
时间复杂度是$ O(nlognlogN) $的,会超时。
从这个方法入手,考虑如何优化。实际上,二分是不必要的。同样我们可以采用two pointers。如果某个时刻线段树中有点等于$ m $。我们完全可以从后端删除点。这是为什么呢?
理由是从后端删除掉的点与其它点产生的共鸣(姑且这么叫吧)肯定比当前的答案要大,若以删除对更好的答案是没有影响的。这样我们可以删除直到线段树中没有点等于$ m $。同时记录答案。
时间复杂度是$ O(nlogn) $的,可以通过所有数据。
代码:
#include<bits/stdc++.h>
using namespace std; const int maxn = ; int n,m,Num; int mp[maxn<<]; struct Dir{
int l,r,len;
}d[maxn]; class SegmentTree{
private:
int lazy[maxn<<],maxx[maxn<<];
void push_down(int now){
maxx[now<<] += lazy[now]; lazy[now<<] += lazy[now];
maxx[now<<|] += lazy[now]; lazy[now<<|] += lazy[now];
lazy[now] = ;
}
void push_up(int now){maxx[now] = max(maxx[now<<],maxx[now<<|]);}
public:
void add(int now,int l,int r,int tl,int tr,int val){
if(lazy[now] && tl != tr) push_down(now);
if(tl >= l && tr <= r){
lazy[now] +=val; maxx[now]+=val;
return;
}
if(tl > r || tr < l) return;
int mid = (tl+tr)/;
add(now<<,l,r,tl,mid,val);
add(now<<|,l,r,mid+,tr,val);
push_up(now);
}
int Query(){return maxx[];}
}T; int cmp(Dir a,Dir b){
if(a.len<b.len) return true;
else return false;
} void read(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d%d",&d[i].l,&d[i].r); d[i].len = d[i].r-d[i].l;
mp[++Num] = d[i].l; mp[++Num] = d[i].r;
}
sort(d+,d+n+,cmp);
sort(mp+,mp+Num+);
Num = unique(mp+,mp+Num+)-mp-;
for(int i=;i<=n;i++){
d[i].l = lower_bound(mp+,mp+Num+,d[i].l)-mp;
d[i].r = lower_bound(mp+,mp+Num+,d[i].r)-mp;
}
} void work(){
int lastans = 2e9;
for(int i=,j=;i<=n;i++){ // two pointers
T.add(,d[i].l,d[i].r,,Num,);
if(T.Query() == m){
while(j <= i){
T.add(,d[j].l,d[j].r,,Num,-);
if(T.Query() != m){
lastans = min(lastans,d[i].len-d[j].len);
j++;
break;
}
j++;
}
}
while(j <= i && d[i].len - d[j].len > lastans){
T.add(,d[j].l,d[j].r,,Num,-);
j++;
}
}
if(lastans == 2e9) puts("-1");
else printf("%d",lastans);
} int main(){
read();
work();
return ;
}