洛谷P1712 [NOI2016]区间【线段树】

时空限制 1000ms-3000ms / 256MB

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

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


题目分析

先将给定区间按区间长度升序排序
像维护队列一样维护区间
初始队列内只有第一个区间,之后不断按顺序从队尾加入区间,并记录每个点被覆盖的次数
若发现某个点被覆盖次数>=m,此时队尾后面的区间一定不能与队头区间组成更优解
所以不断用队尾区间与队头区间更新答案,将队头区间弹出
直到所有点被覆盖次数<m,再从队尾加入区间,如此反复

维护区间加和区间最值的数据结构很容易想到线段树
由于每个区间都只会在入队出队时修改一次,所以总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
注意区间需要离散化


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const lt inf=2e9;
const int maxn=2000010;
int n,m;
int mx[maxn<<2],add[maxn<<2];
struct node{int ll,rr;lt d;}rem[maxn];
int a[maxn],pos[maxn],cnt;
bool cmp(node x,node y){return x.d<y.d;}

void push(int s,int t,int p,int mid)
{
    if(!add[p]) return;
    add[p<<1]+=add[p]; add[p<<1|1]+=add[p];
    mx[p<<1]+=add[p]; mx[p<<1|1]+=add[p];
    add[p]=0;
}

void update(int ll,int rr,int s,int t,int p,int val)
{
    if(ll<=s&&t<=rr){ mx[p]+=val; add[p]+=val; return;}
    int mid=s+t>>1;
    push(s,t,p,mid);
    if(ll<=mid) update(ll,rr,s,mid,p<<1,val);
    if(rr>mid) update(ll,rr,mid+1,t,p<<1|1,val);
    mx[p]=max(mx[p<<1],mx[p<<1|1]);
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
    	int ll=read(),rr=read();
        rem[i].ll=a[++cnt]=ll; 
        rem[i].rr=a[++cnt]=rr;
        rem[i].d=rr-ll;
    }
    
    sort(a+1,a+1+cnt); cnt=0;
    for(int i=1;i<=n*2;++i)
    if(i==1||a[i]!=a[i-1])
    pos[++cnt]=a[i];
    
    for(int i=1;i<=n;++i)
    {
        rem[i].ll=lower_bound(pos+1,pos+1+cnt,rem[i].ll)-pos;
        rem[i].rr=lower_bound(pos+1,pos+1+cnt,rem[i].rr)-pos;
    }
    
    lt ans=inf; int L=1,R=1;
    sort(rem+1,rem+1+n,cmp);
    while(R<=n)
    {
        update(rem[R].ll,rem[R].rr,1,cnt,1,1);
        while(mx[1]>=m){
            ans=min(ans,rem[R].d-rem[L].d);
            update(rem[L].ll,rem[L].rr,1,cnt,1,-1);
            L++;
        }
        R++;
    }
    
    if(ans==inf) printf("-1");
    else printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值