NOI 2016 区间

题目描述:
在数轴上有 n
n个闭区间 [l1,r1],[l2,r2],…,[ln,rn][l1,r1],[l2,r2],…,[ln,rn]。现在要从中选出 m 个区间,使得这 m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。
输入格式
第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n1≤m≤n。
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri为该区间的左右端点。
输出格式
只有一行,包含一个正整数,即最小花费。


【题目分析】
线段树+离散化


【代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct node{
    int l,r;
    int maxx,tag;
}t[8000001];
int len[1000001],ls[2000001],rank[1000001],l[1000001],r[1000001],n,m;
inline bool cmp (int a,int b)
{return len[a]<len[b];}
inline void push(int k)
{
    if (!t[k].tag) return ;
    t[k*2].tag+=t[k].tag;  t[k*2].maxx+=t[k].tag;
    t[k*2+1].tag+=t[k].tag;t[k*2+1].maxx+=t[k].tag;
    t[k].tag=0;
}
inline void add (int k,int l,int r,int opt)
{
    push(k);
    if (t[k].l>=l&&t[k].r<=r) {t[k].tag+=opt;t[k].maxx+=opt;return;}
    int mid=(t[k].l+t[k].r)/2;
    if (l<=mid) add(k*2,l,r,opt);
    if (r>mid) add(k*2+1,l,r,opt);
    t[k].maxx=max(t[k*2].maxx,t[k*2+1].maxx);
}
inline void build (int k,int L,int R)
{
    t[k].l=L;t[k].r=R;
    if (L==R) return ;
    int mid=(L+R)/2;
    build(k*2,L,mid);build(k*2+1,mid+1,R); 
}
int main()
{
    int n,m,top=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) scanf("%d%d",&l[i],&r[i]),ls[++top]=l[i],ls[++top]=r[i],len[i]=r[i]-l[i];
    sort(ls+1,ls+top+1);
    top=unique(ls+1,ls+top+1)-ls-1;

    for (int i=1;i<=n;++i)
    {
//      int a1=l[i],a2=r[i];
        l[i]=lower_bound(ls+1,ls+top+1,l[i])-ls;
        r[i]=lower_bound(ls+1,ls+top+1,r[i])-ls;
//      ys[l[i]]=a1;
//      ys[r[i]]=a2;
    }
    for (int i=1;i<=n;++i) rank[i]=i;
    build(1,1,top);
    sort(rank+1,rank+n+1,cmp);
    int p=1,ans=0x3f3f3f3f;
    for (int i=1;i<=n;++i)
    {
        while (t[1].maxx>=m)
        {
//          cout<<p<<"--"<<i<<" "<<len[rank[i-1]]-len[rank[p]]<<endl;
            ans=min(ans,len[rank[i-1]]-len[rank[p]]);
            add(1,l[rank[p]],r[rank[p]],-1);
            p++;
        }
//      cout<<t[1].maxx<<endl;
//      cout<<"add"<<l[rank[i]]<<" "<<r[rank[i]]<<endl;
        add(1,l[rank[i]],r[rank[i]],1);
    }

    while (t[1].maxx>=m)
    {
//      cout<<p<<"--"<<i<<" "<<len[rank[i-1]]-len[rank[p]]<<endl;
        ans=min(ans,len[rank[n]]-len[rank[p]]);
        add(1,l[rank[p]],r[rank[p]],-1);
        p++;
    }
    if (ans!=0x3f3f3f3f)
    cout<<ans<<endl;
    else cout<<"-1"<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值