BZOJ 4653: [Noi2016]区间 线段树

Description

在数轴上有 n个闭区间 [l1,r1],[l2,r2],…,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。

Input

第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9

Output

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

Sample Input

6 3

3 5

1 2

3 4

2 2

1 5

1 4

Sample Output

2

题解

首先我们将所有区间按照长度排序,然后我们依次扫描每个区间,用线段树找到它第一次有点覆盖次数超过k的另一个区间,更新一下答案,因为左端点递增时右端点也一定递增,所以维护两个指针扫一下即可。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<algorithm>
#include<ctime>
#include<cmath>
using namespace std;
struct xianduan
{
    int l,r,maxx,lazy;
    void add(int val)
    {
        maxx+=val;
        lazy+=val;
    }
}a[5000000];
void make_tree(int o,int l,int r)
{
    a[o].l=l;
    a[o].r=r;
    a[o].maxx=0;
    a[o].lazy=0;
    if(l==r) return;
    int mid=l+r>>1;
    make_tree(2*o,l,mid);
    make_tree(2*o+1,mid+1,r);
}
void push_down(int o)
{
    if(a[o].l==a[o].r) return;
    if(!a[o].lazy) return;
    a[2*o].add(a[o].lazy);
    a[2*o+1].add(a[o].lazy);
    a[o].lazy=0;
}
void change(int o,int l,int r,int d)
{
    if(a[o].r<l || a[o].l>r) return;
    push_down(o);
    if(a[o].l>=l && a[o].r<=r) 
    {
         a[o].add(d);
         return;
    }
    change(2*o,l,r,d);
    change(2*o+1,l,r,d);
    a[o].maxx=max(a[2*o].maxx,a[2*o+1].maxx);
}
struct interval
{
    int l,r,val;
    bool operator < (interval b) const
    {
        return val<b.val;
    }
}q[600000];
int dd[1100000];
int top=0;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].val=q[i].r-q[i].l+1,dd[++top]=q[i].l,dd[++top]=q[i].r;
    sort(dd+1,dd+1+top);
    top=unique(dd+1,dd+1+top)-dd-1;
    for(int i=1;i<=n;i++) q[i].l=lower_bound(dd+1,dd+1+top,q[i].l)-dd,q[i].r=lower_bound(dd+1,dd+1+top,q[i].r)-dd;
    sort(q+1,q+1+n);
    int r=1;
    int ans=2147483647;
    make_tree(1,1,top);
    for(int i=1;i<=n;i++)
    {
        while(a[1].maxx<m && r<=n)
        {
            change(1,q[r].l,q[r].r,1);
            r++;
        }
        if(a[1].maxx<m) break;
        ans=min(ans,q[r-1].val-q[i].val);
        change(1,q[i].l,q[i].r,-1);
    }
    if(ans==2147483647) cout<<-1;
    else cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值