BZOJ 4653: [Noi2016]区间 双指针 + 线段树
只要一堆线段有重叠次数大于等于 $m$ 次的位置,那么一定有解
因为重叠 $m$ 次只需 $m$ 个线断,将那些多余的线断排除掉即可
先将区间按照长度从小到大排序,再用 $two-pointer$ 从左到右扫描
不难发现左右两个指针都是不递减的,所以时间复杂度是 $O(\texttt{nlogn})$ 的
#include <cstdio> #include <string> #include <algorithm> using namespace std; namespace IO { inline void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); } }; const int maxn=500005; const int inf=1000030000; int n,m; namespace tree { int tag[maxn*5],maxv[maxn*5]; inline void pushup(int now) { maxv[now]=tag[now]; maxv[now]+=max(maxv[now<<1],maxv[now<<1|1]); } void update(int l,int r,int now,int L,int R,int v) { if(l>=L&&r<=R) { tag[now]+=v, maxv[now]+=v; return; } int mid=(l+r)>>1; if(L<=mid) update(l,mid,now<<1,L,R,v); if(R>mid) update(mid+1,r,now<<1|1,L,R,v); pushup(now); } int query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) return maxv[now]; int mid=(l+r)>>1,re=0; if(L<=mid) re=max(re,query(l,mid,now<<1,L,R)); if(R>mid) re=max(re,query(mid+1,r,now<<1|1,L,R)); return re+tag[now]; } }; int Arr[maxn*2]; struct Seg { int l,r,len,L,R; }seg[maxn]; bool cmp(Seg a,Seg b) { return a.len<b.len; } int main() { // IO::setIO("input"); scanf("%d%d",&n,&m); int cc=0,i,j; for(i=1;i<=n;++i) { scanf("%d%d",&seg[i].l,&seg[i].r); seg[i].len=seg[i].r-seg[i].l; Arr[++cc]=seg[i].l,Arr[++cc]=seg[i].r; } sort(seg+1,seg+1+n,cmp); sort(Arr+1,Arr+1+cc); for(i=1;i<=n;++i) { seg[i].L=lower_bound(Arr+1,Arr+1+cc,seg[i].l)-Arr; seg[i].R=lower_bound(Arr+1,Arr+1+cc,seg[i].r)-Arr; } int ans=inf; int l=1,r=0; while(l<=n&&r<=n) { if(tree::query(1,cc,1,1,cc)>=m) { ans=min(ans, seg[r].len-seg[l].len); tree::update(1,cc,1,seg[l].L,seg[l].R,-1); ++l; } else { ++r; if(r<=n) tree::update(1,cc,1,seg[r].L,seg[r].R,1); } } printf("%d\n",ans==inf?-1:ans); return 0; }