题目描述

在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) 。现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个位置。换句话说,就是使得存在一个 \(x\) ,使得对于每一个被选中的区间 \([l_i,r_i]\) ,都有 \(l_i≤x≤r_i\) 。

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

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

输入输出格式

输入格式:

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

接下来 \(N\) 行,每行表示一个区间,包含用空格隔开的两个整数 \(l_i\) 和 \(r_i\) 为该区间的左右端点。

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

输出格式:

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

首先,这个区间的数值非常的大,我们需要离散化



可是我QwQ不会呀

区间离散化是将左端点,和右端点放到同一个数组里,排序,然后依次赋值。

记得区间长度计算要在离散化之前

  1. void init()
  2. {
  3. for (int i=1;i<=n;i++)
  4. {
  5. b[++cnt].val=a[i].l;
  6. b[cnt].bel=1;b[cnt].num=i;
  7. b[++cnt].val=a[i].r;
  8. b[cnt].bel=2;b[cnt].num=i;
  9. }
  10. sort(b+1,b+1+cnt,cmp);
  11. for (int i=1;i<=cnt;i++)
  12. {
  13. if (b[i].val!=b[i-1].val) ymh++;
  14. if (b[i].bel==1) l[b[i].num]=ymh;
  15. else r[b[i].num]=ymh;
  16. }
  17. for (int i=1;i<=n;i++)
  18. {
  19. a[i].l=l[i];
  20. a[i].r=r[i];
  21. }
  22. //for (int i=1;i<=n;i++) printf("%d %d\n",a[i].l,a[i].r);
  23. sort(a+1,a+1+n,cmp1);
  24. }

离散化初始化完之后呢~

我们考虑这个题是要求使得最大区间减去最小区间的值最小,那么我们不妨按照区间长度排序

那么,如果判断一个点是否被覆盖了m次呢。

我们可以直接对于每个\([l,r]\)将里面的数都加一,然后统计区间最大值就可以了

那么就需要一个线段树了!



那.....之后呢QwQ,好像没什么用。

这时候就需要一个神奇的东西“尺取法”

尺取法:顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。之所以需要掌握这个技巧,是因为尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的

时候,所以尺取法是一种高效的枚举区间的方法,一般用于求取有一定限制的区间个数或最短的区间等等。当然任何技巧都存在其不足的地方,有些情况下尺取法不可行,无法得出正确答案

尺取法通常适用于选取区间有一定规律,或者说所选取的区间有一定的变化趋势的情况,通俗地说,在对所选取区间进行判断之后,我们可以明确如何进一步有方向地推进区间端点以求解满足条件的区间,如果已经判断了目前所选取的区间,但却无法确定所要求解的区间如何进一步

得到根据其端点得到,那么尺取法便是不可行的。首先,明确题目所需要求解的量之后,区间左右端点一般从最整个数组的起点开始,之后判断区间是否符合条件在根据实际情况变化区间的端点求解答案。

举个例子:

我们要求在给定的序列中求和等于x的区间的个数

那么我们对于当前区间\([l,r]\),如果当前的和小于x,就\(r++\) ,否则\(l++\)

回到这道题

我们先按照长度从小到大依次加入区间,如果当然已经存在一个出现次数为\(m\)

那么就更新答案,并跳\(l\),如果已知出现次数是m,那么就一直更新答案了

否则就一直跳r

QwQ不过跳的时候边界和更新与跳指针的先后要特别注意,详细直接看代码吧

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<map>
  7. #include<queue>
  8. #include<vector>
  9. #include<ctime>
  10. using namespace std;
  11. inline int read()
  12. {
  13. int x=0,f=1;char ch=getchar();
  14. while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
  15. while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  16. return x*f;
  17. }
  18. const int maxn = 500010;
  19. struct Node{
  20. int l,r,len;
  21. };
  22. struct pp{
  23. int val,num,bel;
  24. };
  25. Node a[maxn];
  26. pp b[2*maxn];
  27. int n,m;
  28. int f[10*maxn];
  29. int cnt;
  30. int st,ed;
  31. int add[10*maxn];
  32. int l[maxn],r[maxn];
  33. bool cmp(pp a,pp b)
  34. {
  35. return a.val<b.val;
  36. }
  37. bool cmp1(Node a,Node b)
  38. {
  39. return a.len<b.len;
  40. }
  41. int ymh=0;
  42. void init()
  43. {
  44. for (int i=1;i<=n;i++)
  45. {
  46. b[++cnt].val=a[i].l;
  47. b[cnt].bel=1;b[cnt].num=i;
  48. b[++cnt].val=a[i].r;
  49. b[cnt].bel=2;b[cnt].num=i;
  50. }
  51. sort(b+1,b+1+cnt,cmp);
  52. for (int i=1;i<=cnt;i++)
  53. {
  54. if (b[i].val!=b[i-1].val) ymh++;
  55. if (b[i].bel==1) l[b[i].num]=ymh;
  56. else r[b[i].num]=ymh;
  57. }
  58. for (int i=1;i<=n;i++)
  59. {
  60. a[i].l=l[i];
  61. a[i].r=r[i];
  62. }
  63. //for (int i=1;i<=n;i++) printf("%d %d\n",a[i].l,a[i].r);
  64. sort(a+1,a+1+n,cmp1);
  65. }
  66. void up(int root)
  67. {
  68. f[root]=max(f[root<<1],f[root<<1|1]);
  69. }
  70. void pushdown(int root,int l,int r)
  71. {
  72. if (add[root])
  73. {
  74. add[root<<1]+=add[root];
  75. add[root<<1|1]+=add[root];
  76. f[root<<1]=f[root<<1]+add[root];
  77. f[root<<1|1]=f[root<<1|1]+add[root];
  78. add[root]=0;
  79. }
  80. }
  81. void update(int root,int l,int r,int x,int y,int p)
  82. {
  83. if (x<=l && r<=y)
  84. {
  85. f[root]=f[root]+p;
  86. add[root]+=p;
  87. return;
  88. }
  89. pushdown(root,l,r);
  90. int mid =(l+r) >> 1;
  91. if (x<=mid) update(root<<1,l,mid,x,y,p);
  92. if (y>mid) update(root<<1|1,mid+1,r,x,y,p);
  93. up(root);
  94. }
  95. int query(int root,int l,int r,int x,int y)
  96. {
  97. if (x<=l && r<=y)
  98. {
  99. return f[root];
  100. }
  101. pushdown(root,l,r);
  102. int mid = (l+r) >> 1;
  103. int ans=0;
  104. if (x<=mid) ans=max(ans,query(root<<1,l,mid,x,y));
  105. if (y>mid) ans=max(ans,query(root<<1|1,mid+1,r,x,y));
  106. return ans;
  107. }
  108. int ans=2e9;
  109. int main()
  110. {
  111. n=read(),m=read();
  112. for (int i=1;i<=n;i++)
  113. {
  114. a[i].l=read(),a[i].r=read();
  115. a[i].len=a[i].r-a[i].l;
  116. }
  117. init();
  118. st=1;ed=1;
  119. update(1,1,ymh,a[1].l,a[1].r,1);
  120. while (ed<n && st<n)
  121. {
  122. while (query(1,1,ymh,1,ymh)<m && ed<n && st<n)
  123. {
  124. ++ed;
  125. update(1,1,ymh,a[ed].l,a[ed].r,1);
  126. }
  127. while(query(1,1,ymh,1,ymh)>=m && ed<n && st<n && st<ed)
  128. {
  129. if (query(1,1,ymh,1,ymh)==m)
  130. ans=min(ans,a[ed].len-a[st].len);
  131. update(1,1,ymh,a[st].l,a[st].r,-1);
  132. ++st;
  133. }
  134. }
  135. if (ans==2e9) ans=-1;
  136. cout<<ans<<endl;
  137. return 0;
  138. }

NOI2016区间bzoj4653(线段树,尺取法,区间离散化)的更多相关文章

  1. 2018.08.17 bzoj4653: [Noi2016]区间(线段树+尺取法)

    传送门 将坐标离散化之后直接用尺取法(双指针)+线段树维护. 其实就是说只要目前所有点的被覆盖次数是大于等于m的就移动左指针删除区间更新答案,否则移动右指针加入区间更新答案. 话说忘记排序以及建树的时 ...

  2. P1712-[NOI2016]区间【线段树,尺取法】

    正题 题目链接:https://www.luogu.com.cn/problem/P1712 题目大意 \(n\)个区间,求出其中\(m\)个区间使得它们有覆盖同一个点且最长区间长度减去最短长度最小. ...

  3. 【NOI2016】区间 题解(线段树+尺取法)

    题目链接 题目大意:给定$n$个区间$[l_i,r_i]$,选出$m$个区间使它们有一个共同的位置$x$,且使它们产生的费用最小.求最小费用.费用定义为最长的区间长度减去最短区间长度. ------- ...

  4. luogu 1712 区间(线段树+尺取法)

    题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭 ...

  5. 【洛谷 P1712】 [NOI2016]区间 (线段树+尺取)

    题目链接 emmm看起来好像无从下手, \(l_i,r_i\)这么大,肯定是要离散化的. 然后我们是选\(m\)个区间,我们先对这些区间按长度排个序也不影响. 排序后,设我们取的\(m\)个区间的编号 ...

  6. 【BZOJ4653】【NOI2016】区间(线段树)

    [BZOJ4653][NOI2016]区间(线段树) 题面 BZOJ 题解 \(NOI\)良心送分题?? 既然是最大长度减去最小长度 莫名想到那道反复减边求最小生成树 从而求出最小的比值 所以这题的套 ...

  7. [BZOJ4653][NOI2016]区间 贪心+线段树

    4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MB Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],. ...

  8. 【题解】P1712 [NOI2016]区间(贪心+线段树)

    [题解]P1712 [NOI2016]区间(贪心+线段树) 一个observe是,对于一个合法的方案,将其线段长度按照从大到小排序后,他极差的来源是第一个和最后一个.或者说,读入的线段按照长度分类后, ...

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

    BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...

  10. HDU 1754 I Hate It(线段树单点替换+区间最值)

    I Hate It [题目链接]I Hate It [题目类型]线段树单点替换+区间最值 &题意: 本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,有两个正整数 N 和 M ( 0 ...

随机推荐

  1. Kickstart部署之FTP架构

    原文转自:https://www.cnblogs.com/itzgr/p/10029551.html作者:木二 目录 一 准备 1.1 完整架构:Kickstart+DHCP+VSFTP+TFTP+P ...

  2. Mybatis笔记(1)

    一.Mabits简介 1.1 原始JDBC的分析 问题 ①数据库连接创建.释放频繁造成系统资源浪费从而影响系统性能 ②sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,s ...

  3. Android系统编程入门系列之应用内键值对数据的简单保存

    在应用程序间及与用户的通信交互过程中,会产生并传递一系列数据.针对这些数据,有部分是只在应用程序中使用的缓存数据,还有一部分是在不同位置多次或长时间使用的持久化数据. 对于缓存数据来说,通常以代码中定 ...

  4. docker开启remote-api 2375端口后,Failed to start Docker Application Container Engine,重启docker失败的问题解决

    1.  按照网上的教程修改了 /usr/lib/systemd/system/docerk.service配置后,重启失败.修改/etc/docker/daemon.json 增加hosts后重启也是 ...

  5. docker&flask快速构建服务接口(二)

    系列其他内容 docker快速创建轻量级的可移植的容器✓ docker&flask快速构建服务接口✓ docker&uwsgi高性能WSGI服务器生产部署必备 docker&g ...

  6. 【曹工杂谈】Maven IOC容器的下半场:Google Guice

    Maven容器的下半场:Guice 前言 在前面的文章里,Maven底层容器Plexus Container的前世今生,一代芳华终落幕,我们提到,在Plexus Container退任后,取而代之的底 ...

  7. POJ1426——Find The Multiple (简单搜索+取余)

    题意: 给一个数n,让你找出一个只有1,0,组成的十进制数,要求是找到的数可以被n整除. 用DFS是搜索 当前位数字 (除最高位固定为1),因为每一位都只有0或1两种选择,换而言之是一个双入口BFS. ...

  8. Shell系列(33) - 多分支if语句简介及计算器例子

    多分支if条件语句 if [ 条件判断式1 ] then 当条件判断式1成立时,执行程序1 elif [ 条件判断式2 ] then 当条件判断式2成立时,执行程序2 ...省略更多条件... els ...

  9. virtualbox linux客户机中安装增强功能包缺少kernel头文件问题解决

    linux客户机中安装增强功能包总会提示缺少kernel头文件 根据发行版的不同,用命令行软件包管理命令安装dkms build-essential linux-headers-$(uname -r) ...

  10. Hbuilder 生成移动App资源升级包

    建立文件夹www,将需要更新的文件放置在里面. 将manifest.json文件中version字段的版本修改为新编号. 在文件夹www外建立文件update.xml,内容如下: <?xml v ...