[NOI2016]区间(线段树+尺取法)

[NOI2016]区间(线段树+尺取法)

题面

在数轴上有n个闭区间 。现在要从中选出 m个区间,使得这m个区间共同包含至少一个位置.对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。求所有合法方案中最小的花费。如果不存在合法的方案,输出-1 。

分析

看到长度最大最小值的差,考虑按长度排序。然后类似尺取法的思路,从小到大不断加入区间,直到存在一个位置被覆盖了至少\(m\)次,此时最后一个加入的区间就是当前条件下最大的区间长度。但此时最短区间长度还可以增加,所以不断从头删除区间,保证存在一个位置被覆盖了至少\(m\)次,并更新答案。区间离散化后用线段树维护区间覆盖次数。

正确性证明: 我们只需要证左端点随右端点单调增加即可。假设一个合法的解的区间是\([x,y]\). 假设右端点\(>y\)后,左端点\(p\)\(x\)小,那么会在\([p,y]\)找到更优的解,矛盾

时间复杂度:\(O(n\log n)\)

代码

//[NOI2016]区间 
#include<iostream>
#include<cstdio>
#include<cstring> 
#include<algorithm>
#define INF 0x3f3f3f3f
#define maxn 500000
using namespace std;
struct segment_tree{
    struct node{
        int l;
        int r;
        int val;
        int mark;
        int len(){
            return r-l+1;
        }
    }tree[maxn*2*4+5];
    void push_up(int pos){
        tree[pos].val=max(tree[pos<<1].val,tree[pos<<1|1].val);//找到最大的覆盖次数 
    }
    void build(int l,int r,int pos){
        tree[pos].l=l;
        tree[pos].r=r;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,pos<<1);
        build(mid+1,r,pos<<1|1);
        push_up(pos);
    }
    void push_down(int pos){
        if(tree[pos].mark){
            tree[pos<<1].val+=tree[pos].mark;
            tree[pos<<1].mark+=tree[pos].mark;
            tree[pos<<1|1].val+=tree[pos].mark;
            tree[pos<<1|1].mark+=tree[pos].mark;
            tree[pos].mark=0;
        }
    }
    void update(int L,int R,int val,int pos){
        if(L<=tree[pos].l&&R>=tree[pos].r){
            tree[pos].val+=val;
            tree[pos].mark+=val;
            return;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        if(L<=mid) update(L,R,val,pos<<1);
        if(R>mid) update(L,R,val,pos<<1|1);
        push_up(pos);
    }
    int query_all(){
        return tree[1].val;
    }
}T;

int n,m;
struct seg{
    int len;
    int l;
    int r;
    friend bool operator < (seg p,seg q){
        return p.len<q.len;
    }
}a[maxn+5];
int tmp[maxn*2+5];
int dn=0;
int main(){
#ifndef LOCAL
    freopen("interval.in","r",stdin);
    freopen("interval.out","w",stdout); 
#endif 
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d %d",&a[i].l,&a[i].r);
        tmp[++dn]=a[i].l;
        tmp[++dn]=a[i].r; 
        a[i].len=a[i].r-a[i].l+1;
    }
    sort(tmp+1,tmp+1+dn);
    dn=unique(tmp+1,tmp+1+dn)-tmp-1;
    for(int i=1;i<=n;i++){
        a[i].l=lower_bound(tmp+1,tmp+1+dn,a[i].l)-tmp;
        a[i].r=lower_bound(tmp+1,tmp+1+dn,a[i].r)-tmp;
    }
    sort(a+1,a+1+n);
    T.build(1,dn,1);
    int ans=INF;
    for(int i=1,j=1;i<=n;i++){
        T.update(a[i].l,a[i].r,1,1);
        while(T.query_all()>=m){
            ans=min(ans,a[i].len-a[j].len);
            T.update(a[j].l,a[j].r,-1,1); 
            j++;
        } 
    }
    if(ans==INF) printf("-1\n");
    else printf("%d\n",ans);
}
posted @ 2020-12-03 16:04  birchtree  阅读(62)  评论(0编辑  收藏  举报