【NOI2016】区间

目链接:http://uoj.ac/problem/222

在数轴上有 n 个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x ,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri]的长度定义为 ri?li,即等于它的右端点的值减去左端点的值。

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

输入格式

第一行包含两个正整数 n,m,用空格隔开,意义如上文所述。保证 1≤m≤n。

接下来 n 行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。

输出格式

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

样例一

input

6 3
3 5
1 2
3 4
2 2
1 5
1 4

output

2

explanation

如图,当 n=6, m=3n=6, m=3 时,花费最小的方案是选取 [3,5][3,5] 、[3,4][3,4] 、[1,4][1,4] 这三个区间,他们共同包含了 44 这个位置,所以是合法的。其中最长的区间是 [1,4][1,4] ,最短的区间是 [3,4][3,4] ,所以它的花费是 (4?1)?(4?3)=2(4?1)?(4?3)=2 。

限制与约定

所有测试数据的范围和特点如下表所示:

测试点编号 n m li,ri
1 20 9 0≤li≤ri≤1000
2 10
3 199 3 0≤li≤ri≤100000
4 200
5 1000 2
6 2000
7 199 60 0≤li≤ri≤5000
8 200 50
9 0≤li≤ri≤10^9
10 1999 500 0≤li≤ri≤5000
11 2000 400
12 500 0≤li≤ri≤10^9
13 30000 2000 0≤li≤ri≤100000
14 40000 1000
15 50000 15000
16 100000 20000
17 200000 0≤li≤ri≤10^9
18 300000 50000
19 400000 90000
20 500000 200000

时间限制:3s

空间限制:256MB

题解

首先对li,ri离散化。

如样例

3 5
1 2
3 4
2 2
1 5
1 4

经排序后——1 1 1 2 2 2 3 3 4 4 5 5。(3,5)就变成了(7,11)这样l,r就变成<=500000了。

将每条线段按长度排个序,变成!

2 2

1 2

3 4

3 5

1 4

1 5

接下来,维护一个双指针(two pointer)如

l

1 2 3 4 5 6(线段编号)

r

不断向右移动r直到有个点出现的次数>m为止如

l

1 2 3 4 5 6

r——>r

记录答案

再不断向右移动l,直到没有一个位置出现的次数>m如

l—->l

1 2 3 4 5 6

r——>r

记录答案,以此类推,直到r>n为止。

放代码跑

#include <cstdio>
#include <algorithm>
using namespace std;
struct data{int l,r,len;}a[500010];
struct tree{int l,r,max,lazy;}T[10001000];
int b[1000100],num,i,j,k,n,m,x,y,t;
bool cmp(const data&a,const data&b){return a.len<b.len;}
int find(int x){
    int l=1,r=num,ans;
    while (l<=r){
        int mid=(l+r)>>1;
        if (b[mid]>=x)ans=mid,r=mid-1;else l=mid+1;
    }
    return ans;
}
void build(int i,int l,int r){
    T[i].l=l;T[i].r=r;T[i].max=0;
    if (l==r)return;
    build(i*2,l,(l+r)>>1);build(i*2+1,((l+r)>>1)+1,r);
}
void pushdown(int i){
    if (T[i].lazy){
        T[i*2].max+=T[i].lazy;T[i*2+1].max+=T[i].lazy;
        T[i*2].lazy+=T[i].lazy;T[i*2+1].lazy+=T[i].lazy;
        T[i].lazy=0;
    }
}
void pushup(int i){T[i].max=max(T[i*2].max,T[i*2+1].max);}
void change(int i,int l,int r,int v){
    if (T[i].l==l&&T[i].r==r){T[i].max+=v;T[i].lazy+=v;return;}
    pushdown(i);
    int mid=(T[i].l+T[i].r)>>1;
    if (r<=mid)change(i*2,l,r,v);
    else if (l>mid)change(i*2+1,l,r,v);
    else {change(i*2,l,mid,v);change(i*2+1,mid+1,r,v);}
    pushup(i);
}
int main(){
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        a[i].l=x;a[i].r=y;a[i].len=y-x;
        b[++num]=x;b[++num]=y;
    }
    sort(a+1,a+1+n,cmp);sort(b+1,b+1+num);
    for (i=1;i<=n;i++)a[i].l=find(a[i].l),a[i].r=find(a[i].r);
    build(1,1,num);
    int l=1,r=1,ans=1013687232;
    while(l<=n&&r<=n){
        while (T[1].max<m&&r<=n){change(1,a[r].l,a[r].r,1);r++;}
        if (T[1].max>=m)ans=min(ans,a[r-1].len-a[l].len);
        if (r>n)break;
        change(1,a[l].l,a[l].r,-1);l++;
    }
    printf("%d\n",ans==1013687232?-1:ans);
    return 0;
}
时间: 2024-08-18 22:29:25

【NOI2016】区间的相关文章

【BZOJ4653】[Noi2016]区间 双指针法+线段树

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

[Noi2016]区间[离散化+线段树维护+决策单调性]

4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 621  Solved: 329[Submit][Status][Discuss] Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的

[Noi2016]区间

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

bzoj 4653: [Noi2016]区间

额,是不是一到了晚上IQ就--: 这个题一开始完全没有思路.(貌似脑子就没动一下) %了一下题解. 大概是决策是有单调性的,因为要去区间长度差最小,所以接排个序,然后扫描右端点,找出满足有点被覆盖m次的最右的左端点就好. 然后判断是不是有覆盖m个点的用线段树维护一下. (23333,吐槽,为什么离散化二分的时候,把左端点搞成0,右端点搞成L就会RE??excuse me?!!) 1 #include<bits/stdc++.h> 2 #define LL long long 3 #define

【uoj222】 NOI2016—区间

http://uoj.ac/problem/222 (题目链接) 题意:有n个区间,当有m个区间有公共部分时,求m个区间长度的最大值与最小值之差的最小值. Solution  线段树+滑动窗口.这道题很好做,可是在考场上就差一点点,我愣是没想出来.  先将区间按长度排序,保证它们的长度是递增的,这样就可以滑动窗口了.将区间的端点离散化后,用线段树维护每个节点被覆盖的次数,记录当前区间被覆盖次数最多的点被覆盖多少次,当次数达到要求是更新答案,将头指针向后移动. 代码: // uoj222 #inc

[BZOJ4653][Noi2016]区间

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

UOJ222 NOI2016 区间 线段树+FIFO队列

首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询问覆盖次数可以用线段树实现 1 //C++11 code 2 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 7 const int maxN=500005; 8 const int inf

[UOJ #222][NOI2016]区间(线段树)

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

[Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086

额... 首先,看到这道题,第一想法就是二分答案+线段树... 兴高采烈的认为我一定能AC,之后发现n是500000... nlog^2=80%,亲测可过... 由于答案是求满足题意的最大长度-最小长度最小,那么我们可以考虑将区间按长度排序 之后,因为我们是需要最大最小,所以,我们必定选择在排完序的区间上取连续的一段是最优情况(起码不会比别的差) 因此,考虑双指针扫一下就可以了... 是不是很水? 由于懒得写离散化,一开始写的动态开点线段树,我*****什么鬼?mle?!256mb开不下! lo

BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针

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