[NOI2016] 区间
Link
Solution
答案贡献只与区间长度最大的和最小的有关,故长度介于它们之间的选不选无所谓。再贪一下,发现区间加得越多有可能会更优,那么一定要把所有能加的区间都加进去才最好。所以按长度排序之后,选了的区间一定是连续的一段。
考虑一个朴素的暴力算法,我们枚举最大区间和最小区间,然后把其间的所有区间也加入,把对应位置打上标记。那么只需要知道有没有一个位置被打的标记次数大于等于 \(m\)。复杂度 \(O(n^4)\)。线段树随便维护一下就能优化到 \(O(n^3 \log n)\) ,但是好像没有什么用。
发现枚举是连续的,不用每次全部重新统计。这样可以降到 \(O(n^2 \log n)\),期望得分 60。
瓶颈在于枚举端点。考虑固定右端点,那么最大的能使得标记大于等于 \(m\) 的点存在的左端点可以唯一确定。把右端点往右移动一格时,左端点也会跟着移动,而且我们发现左端点一定是单调的往右移动。这实际上就是一个 2-pointer 。那么复杂度就将为 \(O(n \log n)\) ,期望得分 100。
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 500007
#define lid id<<1
#define rid id<<1|1
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct Seg{
int l,r,len;
bool operator <(const Seg &X)const{return len<X.len;}
}a[N];
struct Node{
int mx,tag,lf,rf;
}t[(N*2)<<2];
int n,m,sz;
inline void update(int id){t[id].mx=max(t[lid].mx,t[rid].mx);}
inline void Push(int id,int val){t[id].mx+=val,t[id].tag+=val;}
inline void pushdown(int id){Push(lid,t[id].tag),Push(rid,t[id].tag),t[id].tag=0;}
int L,R,V;
#define Lf t[id].lf
#define Rf t[id].rf
void modify(int id){
if(L<=Lf&&Rf<=R) Push(id,V);
else{
int mid=(Lf+Rf)>>1;
if(t[id].tag) pushdown(id);
if(L<=mid) modify(lid);
if(R>mid) modify(rid);
update(id);
}
}
void build(int id,int lf,int rf){
t[id].mx=t[id].tag=0;
t[id].lf=lf,t[id].rf=rf;
if(lf==rf) return ;
int mid=(lf+rf)>>1;
build(lid,lf,mid),build(rid,mid+1,rf);
}
inline bool check(int x){
build(1,1,sz);
for(int l=1,r=1;r<=n;r++){
while(a[l].len+x<a[r].len)
L=a[l].l,R=a[l].r,V=-1,modify(1),l++;
L=a[r].l,R=a[r].r,V=1,modify(1);
if(t[1].mx>=m) return 1;
}
return 0;
}
int X[N<<1];
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i].l=read(),a[i].r=read(),a[i].len=a[i].r-a[i].l,
X[(i<<1)-1]=a[i].l,X[i<<1]=a[i].r;
sort(a+1,a+1+n),sort(X+1,X+1+2*n);
sz=unique(X+1,X+1+2*n)-(X+1);
for(int i=1;i<=n;i++)
a[i].l=lower_bound(X+1,X+1+sz,a[i].l)-X,
a[i].r=lower_bound(X+1,X+1+sz,a[i].r)-X;
int l=1,ans=X[sz]+1;
build(1,1,sz);
for(int i=1;i<=n;i++){
L=a[i].l,R=a[i].r,V=1,modify(1);
while(l<i){
L=a[l].l,R=a[l].r,V=-1,modify(1);
if(t[1].mx>=m) l++;
else{V=1,modify(1);break;}
}
if(t[1].mx>=m) ans=min(ans,a[i].len-a[l].len);
}
printf("%d",ans==X[sz]+1? -1:ans);
}