UOJ222 【NOI2016】区间

描述

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

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

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

输入格式

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

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

输出格式

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

样例一

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。

样例二

见样例数据下载。

样例三

见样例数据下载。

限制与约定

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

测试点编号 nn mm li,rili,ri
1 2020 99 0≤li≤ri≤1000≤li≤ri≤100
2 1010
3 199199 33 0≤li≤ri≤1000000≤li≤ri≤100000
4 200200
5 10001000 22
6 20002000
7 199199 6060 0≤li≤ri≤50000≤li≤ri≤5000
8 200200 5050
9 0≤li≤ri≤1090≤li≤ri≤109
10 19991999 500500 0≤li≤ri≤50000≤li≤ri≤5000
11 20002000 400400
12 500500 0≤li≤ri≤1090≤li≤ri≤109
13 3000030000 20002000 0≤li≤ri≤1000000≤li≤ri≤100000
14 4000040000 10001000
15 5000050000 1500015000
16 100000100000 2000020000
17 200000200000 0≤li≤ri≤1090≤li≤ri≤109
18 300000300000 5000050000
19 400000400000 9000090000
20 500000500000 200000200000

时间限制:3S

空间限制:128M

正解:离散化+线段树维护+决策单调性

解题报告:

  NOI2016day2T1,考场上写了个复杂度跟长度有关的算法,期望得分只有50分。

  实际上我想的就是正解了,然而我没有想到优化。实际上区间显然可以离散化,多余部分可以不要,只需要考虑(视为)端点相交即可,也就是中间没有端点的部分直接缩成一个点就可以了。

  考虑答案一定是将区间按长度排序后连续的一段(我暴力里面用了这个思想,mdzz),也就是说具有决策单调性。所以我们只需要快速维护整个区间内被覆盖的最大次数(点被覆盖)。显然线段树直接维护即可。

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 500011;
21 int n,m,cnt,L,ql,qr,val;
22 int p[MAXN*4],ans;
23 struct cha{
24     int l,r,len;
25 }Q[MAXN];
26
27 struct node{
28     int lazy,maxl;
29 }a[MAXN*8];
30
31 inline int getint()
32 {
33        int w=0,q=0;
34        char c=getchar();
35        while((c<‘0‘ || c>‘9‘) && c!=‘-‘) c=getchar();
36        if (c==‘-‘)  q=1, c=getchar();
37        while (c>=‘0‘ && c<=‘9‘) w=w*10+c-‘0‘, c=getchar();
38        return q ? -w : w;
39 }
40
41 inline bool cmp(cha q,cha qq){ return q.len<qq.len;  }
42
43 inline void pushdown(int root,int l,int r){
44     if(!a[root].lazy) return ;
45     int lc=root*2,rc=lc+1;
46     a[lc].lazy+=a[root].lazy; a[rc].lazy+=a[root].lazy;
47     a[lc].maxl+=a[root].lazy; a[rc].maxl+=a[root].lazy;
48     a[root].lazy=0;
49 }
50
51 inline void update(int root,int l,int r){
52     if(ql<=l && r<=qr) { a[root].lazy+=val; a[root].maxl+=val; return ; }
53     pushdown(root,l,r);
54     int mid=(l+r)/2; int lc=root*2,rc=lc+1;
55     if(ql<=mid) update(lc,l,mid); if(qr>mid) update(rc,mid+1,r);
56     a[root].maxl=max(a[lc].maxl,a[rc].maxl);
57 }
58
59 inline void work(){
60     n=getint(); m=getint(); for(int i=1;i<=n;i++) Q[i].l=getint(),Q[i].r=getint(),p[++cnt]=Q[i].l,p[++cnt]=Q[i].r,Q[i].len=Q[i].r-Q[i].l;
61     sort(p+1,p+cnt+1);
62     L=unique(p+1,p+cnt+1)-p-1;
63     for(int i=1;i<=n;i++) Q[i].l=lower_bound(p+1,p+L+1,Q[i].l)-p,Q[i].r=lower_bound(p+1,p+L+1,Q[i].r)-p;//离散化
64     int now=0; sort(Q+1,Q+n+1,cmp);
65     ans=(1<<30);
66     for(int i=1;i<=n;i++) {//决策单调性,一定是连续区间
67     while(a[1].maxl<m) {
68         if(now==n) return ;
69         now++; val=1;  ql=Q[now].l; qr=Q[now].r;
70         update(1,1,L);
71     }
72     ans=min(Q[now].len-Q[i].len,ans);
73     ql=Q[i].l; qr=Q[i].r; val=-1;
74     update(1,1,L);
75     }
76 }
77
78 int main()
79 {
80   work();
81   if(ans==(1<<30)) ans=-1;
82   printf("%d",ans);
83   return 0;
84 }
时间: 2024-08-01 16:08:16

UOJ222 【NOI2016】区间的相关文章

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

【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. 对于一个合法的

【uoj222】 NOI2016—区间

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

[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

[BZOJ4653][Noi2016]区间

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

[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.