洛谷P1712 [NOI2016]区间

noi2016第一题(大概是签到题吧,可我还是不会)


链接在这里

题面可以看链接;

先看题意

这么大的l,r,先来个离散化

很容易,我们可以想到一个结论

假设一个点被覆盖次数大于m

我们将覆盖这个点的区间升序排序;

则所选区间一定是排序后序列中的一个长度为m+1的连续子序列

证明很容易,取更远的点会使最大值更大从而使差值最大

我们可以从这个结论出发,再观察该题所求,符合尺取法的思路

我们考虑用尺取法求解

没了解尺取法的读者可以去自行了解一下

如何求解呢?

我们考虑将区间按权值大小升序排序

从小到大加载到数轴上,统计数轴上点被覆盖的最大次数

当我们将一个区间加载后若被覆盖的最大次数大于m则说明存在符合条件的点

我们区间最大上界已经确定,接着确定下界

将区间由加入顺序向后删除

当删除一个区间后总体max的值要小于m

区间序列的下界便确定了,

目前便得到了有可能更新答案的区间序列的最大值和最小值

在此我对几个点进行解释

1 .首先我们为什么要按权值排序,

原因便是我们一开始就证明过的性质

利用该性质我们可以得到可能更新答案的所有情况从而求解

2 .最大值与最小值之前的区间呢?不会影响答案吗?

不会影响,我们关心的只是符合题意的区间最小值和最大值

只关注边界,至于内部在所选序列中的区间具体是谁我们并不关心

3.我们在确定区间序列下界时将一些区间删掉了

不会对结果有影响吗?

事实上,我们删掉的区间一定是对答案无贡献的

证明很容易

我们删除区间的大小一定小于目前正在寻找的下界

即使之后在加入某个大区间时这个区间产生了贡献成为最小区间

但所加入的最大区间一定大于等于之前的上界

而该区间又小于之前的下界

所以差值一定大于先前的值,故不对最终答案贡献,

有了这些思路后我们就可以做了

至于如何获得当前数轴的最大覆盖次数

和如何将区间加入数轴

我们维护一颗最大值的线段树即可

注意因为我们采取了离散化

所以线段树数组的大小由4倍变为8倍


时间复杂度便是线段树的时间复杂度了

显然是可以过5e5数据的

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn =5e5+1;
int tree[maxn*8];
int add[maxn*8];
inline int read(){
int ret=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
f=-f;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
ret=ret*10+(ch^'0');
ch=getchar();
}
return f*ret;
}
int n,m;
struct edge{
int val;
int num;
}e[maxn];
struct node{
int val;
int num;
}p[maxn*2];
int cnt;
bool cmp(node x,node y){
return x.val<y.val;
}
bool cmp2(edge x,edge y){
return x.val<y.val;
}
int ln[maxn*2];
int rn[maxn*2];
void pushdown(int rt){
if(add[rt]){
int ls=rt*2;
int rs=rt*2+1;
tree[ls]+=add[rt];
tree[rs]+=add[rt];
add[ls]+=add[rt];
add[rs]+=add[rt];
add[rt]=0;
}
return ;
}
void update(int ro,int l,int r,int ls,int rs,int val){
if(rs<l||ls>r){
return ;
}
if(rs>=r&&ls<=l){
tree[ro]+=val;
add[ro]+=val;
return ;
}
int mid=(l+r)>>1;
pushdown(ro);
update(ro*2,l,mid,ls,rs,val);
update(ro*2+1,mid+1,r,ls,rs,val);
tree[ro]=max(tree[ro*2],tree[ro*2+1]);//维护区间最大点值
return ;
}
int main(){
// freopen("a.in","r",stdin);
n=read();
m=read();
int l,r;
for(int i=1;i<=n;i++){
l=read();
r=read();
// cout<<l<<" "<<r<<endl;
e[i].num=i;
e[i].val=r-l;
cnt++;
p[cnt].num=i;
p[cnt].val=l;
cnt++;
p[cnt].num=i;
p[cnt].val=r;
}
int num=0;
sort(p+1,p+1+cnt,cmp);
for(int i=1;i<=cnt;i++){
if(p[i].val!=p[i-1].val){
num++;//num为新建的权值,当相邻值相等时,权值不变
}
int u=p[i].num;//更新原本的L,与r;
if(!ln[u]){
ln[u]=num;
}
else rn[u]=num;
}
sort(e+1,e+1+n,cmp2);
int rig=num;
int ri=0;
int li=0;
int ans=inf;
/*
本题该部分采用了尺取法
当区间内没有点被覆盖次数大于m时,我们加入一个区间进去
如果加入此区间
//cout<<m;
*/
while(true){
while(tree[1]<m&&ri<n){//尺取法,当我们加入一个节点,如果此事有一点被覆盖次数大于等于m,这个区间为合法最大值
ri++;
int u=e[ri].num;
int ls=ln[u];
int rs=rn[u];
update(1,1,rig,ls,rs,1);
}
if(tree[1]<m){
break;//我们即使将所有区间加入也无法大于m故放弃
}
while(tree[1]>=m&&li<n){
li++;
int u=e[li].num;
int ls=ln[u];
int rs=rn[u];
update(1,1,rig,ls,rs,-1);
}//寻找影响这个区间的最小值
ans=min(ans,e[ri].val-e[li].val);// 更新最小值
}
if(ans==inf){
cout<<-1<<endl;
return 0;
}
cout<<ans;
return 0;
}

完结撒花


洛谷P1712 [NOI2016]区间 尺取法+线段树+离散化的更多相关文章

  1. 洛谷 P1712 [NOI2016]区间(线段树)

    传送门 考虑将所有的区间按长度排序 考虑怎么判断点被多少区间覆盖,这个可以离散化之后用一棵权值线段树来搞 然后维护两个指针$l,r$,当被覆盖次数最多的点的覆盖次数小于$m$时不断右移$r$,在覆盖次 ...

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

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

  3. [洛谷P1712] NOI2016 区间

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

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

    先把区间按照长度从小到大排序,然后用尺取法来做 大概就是先一点一点把区间算上 直到某个点被覆盖了m次,然后一点一点把最前面的区间扔掉,直到没有点被覆盖m次,这样反复做(相当于是它选择的区间左右端点在那 ...

  5. luogu P1712 [NOI2016]区间 贪心 尺取法 线段树 二分

    LINK:区间 没想到尺取法. 先说暴力 可以发现答案一定可以转换到端点处 所以在每个端点从小到大扫描线段就能得到答案 复杂度\(n\cdot m\) 再说我的做法 想到了二分 可以进行二分答案 从左 ...

  6. NOI2016区间bzoj4653(线段树,尺取法,区间离散化)

    题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个 ...

  7. codeforces 652C C. Foe Pairs(尺取法+线段树查询一个区间覆盖线段)

    题目链接: C. Foe Pairs time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  8. 洛谷P2023 [AHOI2009]维护序列(线段树区间更新,区间查询)

    洛谷P2023 [AHOI2009]维护序列 区间修改 当我们要修改一个区间时,要保证 \(ax+b\) 的形式,即先乘后加的形式.当将区间乘以一个数 \(k\) 时,原来的区间和为 \(ax+b\) ...

  9. 洛谷P3434 [POI2006]KRA-The Disks(线段树)

    洛谷题目传送门 \(O(n)\)的正解算法对我这个小蒟蒻真的还有点思维难度.洛谷题解里都讲得很好. 考试的时候一看到300000就直接去想各种带log的做法了,反正不怕T...... 我永远只会有最直 ...

随机推荐

  1. 2020-04-28:工作中如何解决MQ消息堆积和消息重复的问题?

    福哥答案2020-04-28:此答案来自群员,感谢群员支持. 消息堆积 只能考虑 增多消费者 以及后端其他服务 组件的吞吐能力 别的有办法吗 如果更彻底一点 分撒单个队列里的消息 队列 更分门别类 或 ...

  2. 2020-04-11:A系统联机同步调用B系统(A和B不是同一公司系统,不能用分布式事务),如何保证系统间数据准实时一致性(设计思路即可)?提醒:需要考虑调用超时、并发、幂等、反交易先到等问题

    福哥答案2020-04-12: 可参考微信支付和支付宝支付.

  3. JavaScript async/await 基础知识

    async 作用: async函数返回一个 Promise对象,无论内部有没有await关键字. await 作用: await等待的是一个表达式,这个表达式的计算结果是 Promise 对象 或者是 ...

  4. Spring/Springboot——JavaConfig

    1.认识JavaConfig JavaConfig是Spring的一个子项目,在Spring4之后成为一个核心功能 JavaConfig中使用的注解: @Configuration 在类上打上这一标签 ...

  5. 设计模式实战系列之@Builder和建造者模式

    前言 备受争议的Lombok,有的人喜欢它让代码更整洁,有的人不喜欢它,巴拉巴拉一堆原因.在我看来Lombok唯一的缺点可能就是需要安装插件了,但是对于业务开发的项目来说,它的优点远远超过缺点. 我们 ...

  6. jQuery的事件与 动画

    什么是事件: 事件的本质是委托. Jquery的 方法: $().css(); $().click(); 等等. 鼠标的事件: 区别在于:mouseover与mouseout再进入或离开后会执行这两个 ...

  7. 使用 JSON 协议的 gRPC

    JSON payload 实现简易的请求和响应的内省. 介绍 大家经常说 gRPC 是基于 Google Protocol Buffers payload 格式的,然而这不完全正确.gRPC payl ...

  8. 精讲RestTemplate第7篇-自定义请求失败异常处理

    本文是精讲RestTemplate第7篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...

  9. 【NOI2005】瑰丽华尔兹 - DP

    题目描述 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意? 众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的 ...

  10. Python 批量保存word

    from docx import Document path = r'D:\pywork\12' # word信息表所在文件夹 w = Document(path + '/' + 'word模板表.d ...