[2019.2.27]BZOJ4653 [Noi2016]区间

首先看到左右端点的范围为\(10^9\),首先离散化。

考虑将所有区间按照原来的长度升序排序,每次将开头一个区间加入,看有没有被大于等于\(m\)个区间覆盖的点,如果有,不停地从末尾删掉区间,直到没有点的覆盖次数大于等于\(m\)的点。那么我们就得到了以我们新加入的那个区间的原长度为最长区间长度,那么使得有点出现次数大于等于\(m\)的最短区间长度的最大值为我们上一个删去的区间,用这两个数的差更新答案。

线段树维护每个点被覆盖的次数即可。

code:

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9+10;
struct s{
    int l,r,sl;
}as[500010];
struct val{
    int v,id,tg;
}v[1000010];
struct node{
    int l,r,mx,tag;
}t[4000010];
int n,m,sz,nw,l,r,tg,ans=INF;
bool cmp(val x,val y){
    return x.v<y.v;
}
bool cmps(s x,s y){
    return x.sl<y.sl;
}
void Upd(int x){
    t[x].mx=max(t[x<<1].mx,t[x<<1|1].mx);
}
void Psd(int x){
    t[x].tag?t[x<<1].mx+=t[x].tag,t[x<<1].tag+=t[x].tag,t[x<<1|1].mx+=t[x].tag,t[x<<1|1].tag+=t[x].tag,t[x].tag=0:0;
}
void Build(int x,int l,int r){
    t[x].l=l,t[x].r=r;
    if(l==r)return;
    int mid=l+r>>1;
    Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
}
void Change(int x,int l,int r,int v){
    if(t[x].l==l&&t[x].r==r)return(void)(t[x].tag+=v,t[x].mx+=v);
    int mid=t[x].l+t[x].r>>1;
    Psd(x);
    r<=mid?Change(x<<1,l,r,v):(l>mid?Change(x<<1|1,l,r,v):(Change(x<<1,l,mid,v),Change(x<<1|1,mid+1,r,v))),Upd(x);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d%d",&v[sz+1].v,&v[sz+2].v),sz+=2,v[sz-1].id=v[sz].id=i,v[sz-1].tg=0,v[sz].tg=1,as[i].sl=v[sz].v-v[sz-1].v;
    sort(v+1,v+sz+1,cmp),v[0].v=-1;
    for(int i=1;i<=sz;++i)v[i].v>v[i-1].v?++nw:0,v[i].tg?as[v[i].id].r=nw:as[v[i].id].l=nw;
    sort(as+1,as+n+1,cmps);
    Build(1,1,nw),l=1;
    for(r=1;r<=n;++r,tg=0){
        Change(1,as[r].l,as[r].r,1);
        while(t[1].mx>=m)tg=1,Change(1,as[l].l,as[l].r,-1),++l;
        ans=min(ans,tg?as[r].sl-as[l-1].sl:INF);
    }
    printf("%d",ans==INF?-1:ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xryjr233/p/BZOJ4653.html