BZOJ4653 NOI2016DAY2T1区间 线段树

33 篇文章 0 订阅
32 篇文章 0 订阅

题目: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
题解:
这道题说实话是算比较基础的题目,,我居然只能想到50+的水法。。看来我线段树还不是很熟。。一开始连题目都读错了,差点没看懂样例。
其实很简单。。
因为l[i],r[i]<=10^9,而最多只有500000个区间,所以我们可以缩点,并不会对答案造成影响。
hash缩完点以后,把用线段树把每一段给出的区间的覆盖次数+1,然后如果有答案的话树根的存储值肯定>=m,此时我们只要把之前的区间一个个减去,直到符合m次覆盖为止,然后更新答案。
不懂缩点的可以在下面留言告诉我,最近常常混迹blog。

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cstring>  
#include<ctime>  
#include<cmath>  
#include<algorithm>  
#include<iomanip>  
#include<bitset>  
#include<set>  
#include<map>  
#include<vector>  
#include<stack>  
#include<queue>  
using namespace std;  
#define MAXN 500010  
#define MAXM 1010  
#define inf 1000000000  
#define MOD 1000000007  
#define eps 1e-8  
#define ll long long  
struct node{  
    int l;  
    int r;  
    int len;  
    friend bool operator <(node x,node y){  
        return x.len<y.len;  
    }  
};  
int n,m;
int tot[MAXN*2],tot1,mx;
node a[MAXN];
map<int,int>h;
int ch[MAXN<<3],tr[MAXN<<3];
int ans=inf*2;
inline void add(int x,int y){
    ch[x]+=y;
    tr[x]+=y;
}
inline void make(int x)
{
    if (ch[x])
    {
        add(x<<1,ch[x]);
        add(x<<1|1,ch[x]);
        ch[x]=0;
    }
}
void change(int z,int x,int y,int l,int r,int cv)
{
    if (x==l&&y==r){
        add(z,cv);
        return ;
    } 
    make(z);
    int mid=x+y>>1;
    if (r<=mid) change(z<<1,x,mid,l,r,cv);
    else if (l>mid) change(z<<1|1,mid+1,y,l,r,cv);
    else{
        change(z<<1,x,mid,l,mid,cv);
        change(z<<1|1,mid+1,y,mid+1,r,cv);
    }
    tr[z]=max(tr[z<<1],tr[z<<1|1]);
} 
int main(){
    int i;
    scanf("%d%d",&n,&m);
    if (!m)
    {
        printf("%d\n",0);
        return 0;
    }
    for (i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].len=a[i].r-a[i].l;
        tot[++tot1]=a[i].l;
        tot[++tot1]=a[i].r;
    }
    sort(tot+1,tot+tot1+1);
    tot[0]=-inf;
    for (i=1;i<=tot1;i++)
    {
        if (tot[i]!=tot[i-1])
        h[tot[i]]=++mx;
    }
    for (i=1;i<=n;i++)
    {
        a[i].l=h[a[i].l];
        a[i].r=h[a[i].r]; 
    } 
    sort(a+1,a+1+n);
    int k=1;
    for (i=1;i<=n;i++)
    {
        change(1,1,mx,a[i].l,a[i].r,1);
        while (tr[1]>=m) 
        {
            ans=min(ans,a[i].len-a[k].len);
            change(1,1,mx,a[k].l,a[k].r,-1);
            k++;
        }
    }
    if(ans==inf*2){  
        ans=-1;  
    }  
    printf("%d\n",ans);
    return 0;
} 

好久没打c++了,打起来有点累。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值