[NOI2016]区间-线段树
一道很巧妙的题。
首先我们需要解决的问题,怎么快速判断选出的m个区间是否存在交。
我们反过来考虑这个问题,
我们每一个选出的区间,就对应的在线段树上区间加1,那么只要存在最大值等于m,就一定有m个区间满足条件了。
那么把区间从小到大排序,一直加到最大值等于m,更新答案,然后删掉最小区间,不停的做下去就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define lson p<<1 #define rson (p<<1)|1 using namespace std; inline int gi () { int x=0, w=0; char ch=0; while (! (ch>='0' && ch<='9') ) { if (ch=='-') w=1; ch=getchar (); } while (ch>='0' && ch<='9') { x= (x<<3) + (x<<1) + (ch^48); ch=getchar (); } return w?-x:x; } const int N=5e5+10; int n,m,head=1,Ans=0x3f3f3f3f,Num,Ls[N<<1],LS[N<<1],Cnt[N<<3],Tag[N<<3]; struct Sect { int l, r, val; }s[N]; bool Cmp (Sect a, Sect b) { return a.val<b.val; } void Pushup (int p) { Cnt[p]=max (Cnt[lson], Cnt[rson]); } void Pushdown (int p) { if (!Tag[p]) return; Tag[lson]+=Tag[p]; Tag[rson]+=Tag[p]; Cnt[lson]+=Tag[p]; Cnt[rson]+=Tag[p]; Tag[p]=0; } void Modify (int p, int l, int r, int Ml, int Mr, int val) { if (l>=Ml && Mr>=r) { Cnt[p]+=val; Tag[p]+=val; return; } Pushdown (p); int Mid= (l+r) >> 1; if (Ml<=Mid) Modify (lson, l, Mid, Ml, Mr, val); if (Mr>Mid) Modify (rson, Mid+1, r, Ml, Mr, val); Pushup (p); } int main () { n=gi (), m=gi (); memset (Ls, -1, sizeof (Ls) ); memset (LS, -1, sizeof (LS) ); for (int i=1;i<=n;++i) { s[i].l=gi (), s[i].r=gi (); s[i].val=s[i].r-s[i].l; Ls[++Num]=s[i].l, Ls[++Num]=s[i].r; } sort (Ls+1, Ls+Num+1); for (int i=1, j=0;i<=Num;++i) { if (Ls[i]!=Ls[i-1]) LS[++j]=Ls[i]; if (i==Num) { Num=j; break; } } for (int i=1;i<=n;++i) { s[i].l=lower_bound (LS+1, LS+Num+1, s[i].l) -LS; s[i].r=lower_bound (LS+1, LS+Num+1, s[i].r) -LS; } sort (s+1, s+n+1, Cmp); for (int i=1;i<=n;++i) { Modify (1, 1, Num, s[i].l, s[i].r, 1); while (Cnt[1]==m) { Ans=min (Ans, s[i].val-s[head].val); Modify (1, 1, Num, s[head].l, s[head].r, -1); head++; } } printf ("%d\n", Ans==0x3f3f3f3f?-1:Ans); return 0; }