【noi 2016】区间

题目描述

在数轴上有NN 个闭区间[li,ri] 。现在要从中选出M 个区间,使得这M 个区间共同包含至少一个位置。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间[l,r]的长度定义为r-l,即等于它的右端点的值减去左端点的值。

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

输入格式:
第一行包含两个正整数N,MN,M 用空格隔开,意义如上文所述。保证1≤M≤N1≤M≤N
接下来NN 行,每行表示一个区间
N<=500000,M<=200000,0≤li≤ri≤10^9N<=500000,M<=200000,0≤li≤ri≤10
9
输出格式:
只有一行,包含一个正整数,即最小花费。

这道题区间范围那么大,首先肯定要离散化,感觉还要按长度排个序…
一开始的想法是枚举l,用一颗线段树做区间+1,直到最大值等于m停止。
但是这样做是n^2logn的…
仔细观察一下发现随着l的右移r也是单调的。
所以就是尺取法啦。
复杂度O(nlogn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#define N 500010
#define M 200010
using namespace std;
struct node{
    int l,r,len;
}b[N];
int Max[8*N],add[8*N],a[2*N];
map <int,int> mp;
bool cmp(node a,node b)
{
    return a.len<b.len;
}
void push_up(int root)
{
    Max[root]=max(Max[root<<1],Max[root<<1|1]);
}
void push_down(int root)
{
    Max[root<<1]+=add[root];
    Max[root<<1|1]+=add[root];
    add[root<<1]+=add[root];
    add[root<<1|1]+=add[root];
    add[root]=0;
}
void update(int root,int l,int r,int x,int y,int w)
{
    if(x<=l&&y>=r) 
    {
        add[root]+=w;
        Max[root]+=w;
        return;
    }
    int mid=(l+r)>>1;
    push_down(root);
    if(x<=mid) update(root<<1,l,mid,x,y,w);
    if(y>mid) update(root<<1|1,mid+1,r,x,y,w);
    push_up(root);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&b[i].l,&b[i].r);
        a[i]=b[i].l,a[i+n]=b[i].r;
        b[i].len=b[i].r-b[i].l;
    }
    sort(a+1,a+2*n+1);
    for(int i=1;i<=2*n;i++) mp[a[i]]=i;
    for(int i=1;i<=n;i++) b[i].l=mp[b[i].l],b[i].r=mp[b[i].r];
    sort(b+1,b+n+1,cmp);
    int j=1;
    update(1,1,2*n,b[1].l,b[1].r,1);
    int ans=1e9;
    for(int i=1;i<=n;i++)
    {
        while(j<n&&Max[1]<m)
        {
            j++;
            update(1,1,2*n,b[j].l,b[j].r,1);
        }
        if(Max[1]<m) break;
        ans=min(ans,b[j].len-b[i].len);
        update(1,1,2*n,b[i].l,b[i].r,-1);
    }
    printf("%d\n",ans==1e9?-1:ans);
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值