题目

在数轴上有 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 为该区间的左右端点。

N<=500000,M<=200000,0≤li≤ri≤10^9

输出格式

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

输入样例

6 3

3 5

1 2

3 4

2 2

1 5

1 4

输出样例

2

题解

比较容易想到,如果我们对所选的区间都+1,那么一定存在某个位置的值>=m

先对区间离散化

这样我们可以得到一个\(O(n^3)\)的算法

区间修改求最值,离散化后用线段树优化,可以做到\(O(n^2logn)\)

为了进一步减小一个n,我们将区间按区间长度排序

从小开始逐个加入线段树中,直到出现>=m的位置,再尝试从小开始从线段树中删除,直至满足>=m时再移动右端点

可以证明这样做的正确性

因为区间过多不影响存在某位置>=m,通过不断移动右端点,有解一定可以判出

设当前解为len,如果不移动左端点,右端点移动的结果一定不会比len要小

所以在满足条件的情况下应尽量移动左端点

\(O(nlogn)\)

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<algorithm>
  6. #define LL long long int
  7. #define REP(i,n) for (int i = 1; i <= (n); i++)
  8. #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
  9. #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
  10. #define ls (u << 1)
  11. #define rs (u << 1 | 1)
  12. using namespace std;
  13. const int maxn = 1000005,maxm = 500005,INF = 1000000000;
  14. inline int read(){
  15. int out = 0,flag = 1; char c = getchar();
  16. while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
  17. while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
  18. return out * flag;
  19. }
  20. struct node{int l,r;}e[maxm];
  21. int b[maxn],bi,tot = 1,n,m,ans = INF;
  22. int mx[4 * maxn],tag[4 * maxn];
  23. inline bool operator <(const node& a,const node& x){
  24. return (b[a.r] - b[a.l]) < (b[x.r] - b[x.l]);
  25. }
  26. int getn(int x){return lower_bound(b + 1,b + 1 + tot,x) - b;}
  27. void pd(int u){
  28. if (tag[u]){
  29. mx[ls] += tag[u]; mx[rs] += tag[u];
  30. tag[ls] += tag[u]; tag[rs] += tag[u];
  31. tag[u] = 0;
  32. }
  33. }
  34. void modify(int u,int l,int r,int L,int R,int x){
  35. if (l >= L && r <= R){
  36. mx[u] += x; tag[u] += x;
  37. return;
  38. }
  39. pd(u);
  40. int mid = l + r >> 1;
  41. if (mid >= L) modify(ls,l,mid,L,R,x);
  42. if (mid < R) modify(rs,mid + 1,r,L,R,x);
  43. mx[u] = max(mx[ls],mx[rs]);
  44. }
  45. int main(){
  46. n = read(); m = read();
  47. for (int i = 1; i <= n; i++)
  48. b[++bi] = e[i].l = read(),b[++bi] = e[i].r = read();
  49. sort(b + 1,b + 1 + bi);
  50. for (int i = 2; i <= bi; i++) if (b[i] != b[tot]) b[++tot] = b[i];
  51. for (int i = 1; i <= n; i++)
  52. e[i].l = getn(e[i].l),e[i].r = getn(e[i].r);
  53. sort(e + 1,e + 1 + n);
  54. modify(1,1,tot,e[1].l,e[1].r,1);
  55. int l = 1,r = 1,L,R;
  56. L = R = b[e[1].r] - b[e[1].l];
  57. if (mx[1] >= m) ans = min(ans,R - L);
  58. while (r <= n){
  59. while (mx[1] >= m && l < r){
  60. modify(1,1,tot,e[l].l,e[l].r,-1);
  61. l++;
  62. L = b[e[l].r] - b[e[l].l];
  63. if (mx[1] >= m) ans = min(ans,R - L);
  64. }
  65. r++;
  66. if (r > n) break;
  67. R = b[e[r].r] - b[e[r].l];
  68. modify(1,1,tot,e[r].l,e[r].r,1);
  69. if (mx[1] >= m) ans = min(ans,R - L);
  70. }
  71. printf("%d\n",ans == INF ? -1 : ans);
  72. return 0;
  73. }

NOI2016 区间 【线段树】的更多相关文章

  1. [NOI2016]区间 线段树

    [NOI2016]区间 LG传送门 考虑到这题的代价是最长边减最短边,可以先把边按长度排个序,双指针维护一个尺取的过程,如果存在包含某个点的区间数\(\ge m\),就更新答案并把左指针右移,这样做的 ...

  2. Luogu P1712 [NOI2016]区间(线段树)

    P1712 [NOI2016]区间 题意 题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间, ...

  3. UOJ222 NOI2016 区间 线段树+FIFO队列

    首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询 ...

  4. BZOJ.4653.[NOI2016]区间(线段树)

    BZOJ4653 UOJ222 考虑二分.那么我们可以按区间长度从小到大枚举每个区间,对每个区间可以得到一个可用区间长度范围. 我们要求是否存在一个点被这些区间覆盖至少\(m\)次.这可以用线段树区间 ...

  5. BZOJ4653 [NOI2016]区间 [线段树,离散化]

    题目传送门 区间 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就 ...

  6. BZOJ4653: [Noi2016]区间(线段树 双指针)

    题意 题目链接 Sol 按照dls的说法,一般这一类的题有两种思路,一种是枚举一个点\(M\),然后check它能否成为答案.但是对于此题来说好像不好搞 另一种思路是枚举最小的区间长度是多少,这样我们 ...

  7. 洛谷$P1712\ [NOI2016]$区间 线段树

    正解:线段树 解题报告: 传送门$QwQ$ $umm$很久以前做的了来补个题解$QwQ$ 考虑给每个区间按权值($r-l$从大往小排序,依次加入,然后考虑如果有一个位置被覆盖次数等于$m$了就可以把权 ...

  8. BZOJ4653:[NOI2016]区间(线段树)

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

  9. hdu 1540 Tunnel Warfare (区间线段树(模板))

    http://acm.hdu.edu.cn/showproblem.php?pid=1540 Tunnel Warfare Time Limit: 4000/2000 MS (Java/Others) ...

  10. BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

    题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...

随机推荐

  1. 《队长说得队》【Alpha】Scrum meeting 3

    项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十二 团队作业8:软件测试与ALPHA冲刺 团队名称 ...

  2. Shell脚本调用ftp上传文件

    Shell脚本调用ftp上传文件 1.脚本如下 ftp -n<<! open x.x.x.x ###x.x.x.x为ftp地址 user username password ###user ...

  3. 将unity3d项目嵌入到Android App中使用

    创建一个新的AndroidStudio app项目. 1.添加库文件:拷贝unity安装目录下的库文件:Unity\Editor\Data\PlaybackEngines\AndroidPlayer\ ...

  4. C/C++基础知识:函数指针和指针函数的基本概念

    [函数指针] 在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址.可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指 ...

  5. Git - revert详解

    git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销作为一次最新的提交    * git revert HEAD                ...

  6. Linux常用文档操作命令--2

    4.文档压缩与解压操作 在Linux中常见的压缩文件有:*.tar.gz.*.tgz.*.gz.*.Z.*bz2等.其每种不同的压缩文件对印的压缩和解压命令也不同. *.tar.gz :tar程序打包 ...

  7. v2ex站长专访 - 100offer专访Livid:不仅仅是V站站长

    转载自: https://www.douban.com/group/topic/121611313/ 前几天上网时偶然发现v2ex站长的blog(https://livid.v2ex.com/),了解 ...

  8. 怎么删除服务中的mysql服务

    可以进WINDOWS的管理里查看MYSQL的服务,把它停止或以DOS下用命令停止1.如果要卸载MYSQL执行下面命令:DOS下>mysqld -remove mysql2.启动MYSQL: DO ...

  9. Leetcode 75. 颜色分类

    题目链接 https://leetcode-cn.com/problems/sort-colors/description/ 题目描述 给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们 ...

  10. SQL_2_查询Select语句的使用

    查询一词在SQL中并不是很恰当,在SQL中查询除了向数据库提出问题之外,还可以实现下面的功能: 1>建立或删除一个表 2>插入.修改.或删除一个行或列 3>用一个特定的命令从几个表中 ...