【[NOI2016]区间】
发现自己的离散化姿势一直有问题
今天终于掌握了正确的姿势
虽然这并不能阻挡我noip退役爆零的历史进程
还是先来看看离散化怎么写吧,我以前都是这么写的
for(std::set<int>::iterator it=s.begin();it!=s.end();it++)
ma[*it]=++tot;
这是使用\(set+map\)的离散化,但是显然巨大的常数是极大的劣势
正确的操作应该是这个样子
std::sort(a+1,a+n+1)
int tot=unique(a+1,a+n+1)-a-1;
for(re int i=1;i<=tot;i++)
ma[a[i]]=i;
\(unique\)能将一个有序数组去重,返回值是去重之后的尾地址,减去首地址就可以得到去重之后的数量了
之后再来看这道题,也是一道非常神的题
这道题告诉我们暴力通向错误的解,错误的解往往跟正解很接近了
首先\(O(n^2logn)\)还是比较好想的做法,我们先将所有的区间按照长度排序,之后我们定住一个区间作为长度最小的区间,强行套用线段树暴力覆盖之后的比它大的区间,直到这个区间有一个点被覆盖了\(m\)次,那么最后覆盖上去的区间的长度减去定住的最小值就是答案了
对所有的答案取一个\(min\)就好了
这样是显然不行的呀,我们得想个办法
于是我就想出来一种显然错误的做法
我大胆的猜测答案是单调的
少年谁给你的勇气
于是这个显然错误的做法是这样的,先将最短的区间一直覆盖,直到覆盖到符合条件为止,之后由于所谓的"答案单调",那么使下一个次短的区间符合条件的区间一定在前面的答案的后面
这样的复杂度均摊下来是对的
答案单调很有道理,但是凉凉了
很容易就能找到反例了
我们再覆盖第一个最短的区间的时候,由于我们只是在判断最短的区间是否满足条件,所以可能这个时候之后的某个区间就突然满足条件了,所以这个答案显然不是单调的
但是这个错误的思路和正解已经非常接近了,差别只有一点,我们判断的时候判断的不是最短的区间是否符合条件,而是判断整个区间内是否有点被覆盖了\(m\)次
这有什么道理呢,其实联想一下尺取法,和尺取法差不多
我们从最短的区间开始覆盖,可能覆盖的过程中满足条件的点并没有来自当前的区间,但是没有关系,我们直接用当前的区间作为最小的区间就行了,这样显然只会导致答案偏大,而真正的最小区间我们在后面也会取到
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<map>
#define maxn 500005
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
std::set<int> s;
std::map<int,int> ma;
struct node
{
int ll,rr,len;
int L,R;
}a[maxn];
int b[maxn<<1];
int n,m,N;
int ans=19999999999;
int l[4000005],r[4000005],d[4000005],tag[4000005];
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
inline int cmp(node K,node M)
{
return K.len<M.len;
}
void build(int x,int y,int i)
{
l[i]=x;
r[i]=y;
if(x==y) return;
int mid=x+y>>1;
build(x,mid,i<<1),build(mid+1,y,i<<1|1);
}
inline void pushdown(int i)
{
if(!tag[i]) return;
tag[i<<1]+=tag[i];
tag[i<<1|1]+=tag[i];
d[i<<1|1]+=tag[i];
d[i<<1]+=tag[i];
tag[i]=0;
}
void change(int x,int y,int v,int i)
{
if(x<=l[i]&&y>=r[i])
{
d[i]+=v;
tag[i]+=v;
return;
}
pushdown(i);
int mid=l[i]+r[i]>>1;
if(y<=mid) change(x,y,v,i<<1);
else if(x>mid) change(x,y,v,i<<1|1);
else change(x,y,v,i<<1),change(x,y,v,i<<1|1);
d[i]=max(d[i<<1],d[i<<1|1]);
}
int main()
{
n=read();
m=read();
for(re int i=1;i<=n;i++)
a[i].ll=read(),a[i].rr=read(),a[i].len=a[i].rr-a[i].ll,b[++N]=a[i].ll,b[++N]=a[i].rr;
std::sort(a+1,a+n+1,cmp);
std::sort(b+1,b+N+1);
int tot=std::unique(b+1,b+N+1)-b-1;
for(re int i=1;i<=tot;i++)
ma[b[i]]=i;
build(1,tot,1);
for(re int i=1;i<=n;i++) a[i].L=ma[a[i].ll],a[i].R=ma[a[i].rr];
int now=-1;
for(re int i=1;i<=n;i++)
{
change(a[i].L,a[i].R,1,1);
if(d[1]==m)
{
ans=a[i].len-a[1].len;
now=i;
break;
}
}
if(now==-1)
{
puts("-1");
return 0;
}
for(re int i=2;i<=n;i++)
{
change(a[i-1].L,a[i-1].R,-1,1);
while(d[1]<m)
{
if(now==n)
{
printf("%d\n",ans);
return 0;
}
now++;
change(a[now].L,a[now].R,1,1);
}
ans=min(ans,a[now].len-a[i].len);
}
printf("%d\n",ans);
return 0;
}
【[NOI2016]区间】的更多相关文章
- BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针
BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...
- [Noi2016]区间[离散化+线段树维护+决策单调性]
4653: [Noi2016]区间 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 621 Solved: 329[Submit][Status][D ...
- [NOI2016]区间 线段树
[NOI2016]区间 LG传送门 考虑到这题的代价是最长边减最短边,可以先把边按长度排个序,双指针维护一个尺取的过程,如果存在包含某个点的区间数\(\ge m\),就更新答案并把左指针右移,这样做的 ...
- [BZOJ4653][NOI2016]区间 贪心+线段树
4653: [Noi2016]区间 Time Limit: 60 Sec Memory Limit: 256 MB Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],. ...
- 【BZOJ4653】[Noi2016]区间 双指针法+线段树
[BZOJ4653][Noi2016]区间 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含 ...
- [NOI2016]区间 题解(决策单调性+线段树优化)
4653: [Noi2016]区间 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1593 Solved: 869[Submit][Status][ ...
- Luogu P1712 [NOI2016]区间(线段树)
P1712 [NOI2016]区间 题意 题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间, ...
- 【题解】P1712 [NOI2016]区间(贪心+线段树)
[题解]P1712 [NOI2016]区间(贪心+线段树) 一个observe是,对于一个合法的方案,将其线段长度按照从大到小排序后,他极差的来源是第一个和最后一个.或者说,读入的线段按照长度分类后, ...
- 洛谷P1712 [NOI2016]区间 尺取法+线段树+离散化
洛谷P1712 [NOI2016]区间 noi2016第一题(大概是签到题吧,可我还是不会) 链接在这里 题面可以看链接: 先看题意 这么大的l,r,先来个离散化 很容易,我们可以想到一个结论 假设一 ...
- BZOJ4653: [Noi2016]区间
传送门 UOJ上卡掉一个点,COGS上卡掉两个点..弃疗,不改了,反正BZOJ上过啦hhh 先把区间按长度递增排序.然后每次用线段树维护区间最大覆盖次数,用一个指针随便扫扫就行了. //NOI 201 ...
随机推荐
- 很小的一个函数执行时间调试器Timer
对于函数的执行性能(这里主要考虑执行时间,所耗内存暂不考虑),这里写了一个简单的类Timer,用于量化函数执行所耗时间. 整体思路很简单,就是new Date()的时间差值.我仅仅了做了一层简单的封装 ...
- Java类加载器ClassLoader总结
JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显 ...
- DataTables添加额外的查询参数和删除columns等无用参数
//1.定义全局变量 var iStart = 0, searchParams={}; //2.配置datatable的ajax配置项 "ajax": { "url&qu ...
- canal —— 阿里巴巴mysql数据库binlog的增量订阅&消费组件
阿里巴巴mysql数据库binlog的增量订阅&消费组件canal ,转载自 https://github.com/alibaba/canal 最新更新 canal QQ讨论群已经建立,群号 ...
- SQL SERVER 2012修改数据库名称(包括 db.mdf 名称的修改)
假设原来数据库名为db,附加数据库为db.mdf和db_log.ldf.需要改成dbt,及dbt.mdf和dbt_log.ldf. 步骤: .首先把原来的数据库进行备份(选择数据库->右键-&g ...
- 如何正确实现 IDisposable 接口
MSDN建议按照下面的模式实现IDisposable接口: public class Foo: IDisposable { public void Dispose() { Dispose(true); ...
- NOIP2017:逛公园
Sol 发现\(NOIP2017\)还没\(AK\)??? 赶紧改 考场上明明打出了\(DP\),没时间了,没判环,重点是没初始化数组,爆\(0\) \(TAT\) 先最短路,然后\(f[i][j]\ ...
- ActiveX界面已显示,调用方法报undefined的处理办法
1.在ie中将当前网址加入信任网站 2.设置->internet选项->安全->受信任站点->自定义级别:将所有有关ActiveX的选项设置为启用 3.重启ie再次访问即可. ...
- response.setHeader()下载的用法
1. HTTP消息头 (1)通用信息头 即能用于请求消息中,也能用于响应信息中,但与被传输的实体内容没有关系的信息头,如Data,Pragma 主要: Cache-Control , Connecti ...
- CentOS新增硬盘,重新扫描总线
Centos 新增硬盘以后,系统不能自动进行识别. 1. 由于不知道新增硬盘挂载的位置,可以先查看现有硬盘挂载的适配器. [root@localhost ~]# ls -l /sys/block/sd ...