【线段树】[NOI2016]区间

题目描述

在数轴上有 n 个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x ,使得对于每一个被选中的区间 [li,ri],都有 lixri

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 rili ,即等于它的右端点的值减去左端点的值。

求所有合法方案中最小的花费。如果不存在合法的方案,输出 1

输入格式

第一行包含两个正整数 n,m ,用空格隔开,意义如上文所述。保证 1mn

接下来 n 行,每行表示一个区间,包含用空格隔开的两个整数 li ri 为该区间的左右端点。

输出格式

只有一行,包含一个正整数,即最小花费。

样例一

input
6 3
3 5
1 2
3 4
2 2
1 5
1 4

output
2

explanation

样例图

如图,当 n=6, m=3 时,花费最小的方案是选取 [3,5] [3,4] [1,4] 这三个区间,他们共同包含了 4 这个位置,所以是合法的。其中最长的区间是 [1,4],最短的区间是 [3,4] ,所以它的花费是 (41)(43)=2

样例二

见样例数据下载。

样例三

见样例数据下载。

限制与约定

所有测试数据的范围和特点如下表所示:

测试点编号 n m li,ri
1 20 9 0liri100
2 10
3 199 3 0liri100000
4 200
5 1000 2
62000
7 199 60 0liri5000
8 200 50
9 0liri109
10 1999 500 0liri5000
11 2000 400
12 500 0liri109
13 30000 2000 0liri100000
14 40000 1000
15 50000 15000
16 100000 20000
17 200000 0 \le l_i \le r_i \le 10^9 0liri109
18 300000 50000
19 400000 90000
20 500000 200000

时间限制: 3s

空间限制: 256MB

下载

样例数据下载



分析

离散化,按照区间长度排序,维护一个双指针,滑动窗口,用一个线段树来维护点被覆盖的次数即可。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 500000
int r[MAXN*2+10],rcnt,m,n,ans=0x7fffffff;
struct itv{
    int l,r,len;
    inline itv(){
    }
    inline itv(int l,int r):l(l),r(r),len(r-l){
    }
    bool operator<(const itv &b)const{
        return len<b.len;
    }
}a[MAXN+10];
struct node{
    int tagp,tag0,mx;
}tree[MAXN*8+10];
inline void push_down(int i){
    if(tree[i].tag0){
        tree[i<<1].tag0=tree[(i<<1)|1].tag0=1;
        tree[i<<1].mx=tree[i<<1].tagp=0;
        tree[(i<<1)|1].mx=tree[(i<<1)|1].tagp=0;
        tree[i].tag0=0;
    }
    if(tree[i].tagp){
        tree[i<<1].mx+=tree[i].tagp,tree[(i<<1)].tagp+=tree[i].tagp;
        tree[(i<<1)|1].mx+=tree[i].tagp,tree[(i<<1)|1].tagp+=tree[i].tagp;
        tree[i].tagp=0;
    }
}
inline void update(int i){
    tree[i].mx=max(tree[i<<1].mx,tree[(i<<1)|1].mx);
}
void insert(int i,int l,int r,int ll,int rr,int d){
    if(ll<=l&&r<=rr){
        tree[i].tagp+=d;
        tree[i].mx+=d;
        return;
    }
    if(ll>r||rr<l)
        return;
    int mid((l+r)>>1);
    push_down(i);
    insert(i<<1,l,mid,ll,rr,d);
    insert((i<<1)|1,mid+1,r,ll,rr,d);
    update(i);
}
int get_mx(int i,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr)
        return tree[i].mx;
    if(ll>r||rr<l)
        return 0;
    int mid((l+r)>>1);
    push_down(i);
    return max(get_mx(i<<1,l,mid,ll,rr),get_mx((i<<1)|1,mid+1,r,ll,rr));
}
void Read(int &x){
    static char c;
    bool f(0);
    while(c=getchar(),c!=EOF){
        if(c=='-')
            f=1;
        else if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            if(f)
                x=-x;
            return;
        }
    }
}
void read(){
    Read(n),Read(m);
    int i,L,R;
    for(i=1;i<=n;i++){
        Read(L),Read(R);
        r[++rcnt]=L,r[++rcnt]=R;
        a[i]=itv(L,R);
    }
    sort(r+1,r+rcnt+1);
    rcnt=unique(r+1,r+rcnt+1)-r-1;
    for(i=1;i<=n;i++){
        a[i].l=lower_bound(r+1,r+rcnt+1,a[i].l)-r;
        a[i].r=lower_bound(r+1,r+rcnt+1,a[i].r)-r;
    }
    sort(a+1,a+n+1);
}
void solve(){
    int i=1,j=2;
    tree[1].tag0=1,tree[1].tagp=tree[1].mx=0;
    insert(1,1,rcnt,a[1].l,a[1].r,1);
    while(i<=n){
        while(j<=n&&tree[1].mx<m){
            insert(1,1,rcnt,a[j].l,a[j].r,1);
            j++;
        }
        if(tree[1].mx<m)
            break;
        ans=min(ans,a[j-1].len-a[i].len);
        insert(1,1,rcnt,a[i].l,a[i].r,-1);
        i++;
    }
}
int main()
{
    read();
    solve();
    if(ans==0x7fffffff)
        puts("-1");
    else
        printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值