洛谷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. PYTHON实战完整教程1-配置VSCode开发环境

    一.安装 为降低学习门槛,保证学习目标的聚焦,我们在windows(使用WinServer2019虚拟机)上搭建开发环境.(系列教程最后结束时,也会部署到linux上) 打开Python官网 http ...

  2. 2020-05-07:具体讲一下CMS流程

    福哥答案2020-05-07: 福哥口诀法:C初并重清(初始标记.并发标记.重新标记.并发清除) 整个过程分为 4 个步骤,包括:初始标记:仅仅只是标记一下 GCRoots 能直接关联到的对象,速度很 ...

  3. C#LeetCode刷题-排序

    排序篇 # 题名 刷题 通过率 难度 56 合并区间   31.2% 中等 57 插入区间   30.4% 困难 75 颜色分类   48.6% 中等 147 对链表进行插入排序   50.7% 中等 ...

  4. JavaScript 循环数组的时候调用方法中包含Promise的时候如何做到串行

    forEach是不能阻塞的, 默认[并行]方式 const list = [1, 2, 3] const square = num => { return new Promise((resolv ...

  5. BLE GAP 协议和 GATT 协议

    BLE GAP 协议和 GATT 协议 最近要打算学习 Blufi 协议进行蓝牙配置,其中必然使用 GAP 协议和 GATT 协议,于是进行重新学习一番. BLE 是一个 Bluetooth SIG ...

  6. 微信小程序扫码解析小程序码

    通过微信扫小程序码,跳转到应用小程序内, 如何解析小程序码的参数呢? 一般小程序码会跳转到设置的页面,如首页, 可以直接跳转到小程序首页,然后解析小程序携带的参数,再打开某个页面. (小程序码的路径要 ...

  7. excel表格,根据某一列的值对整行进行颜色填充

    1.选中要影响的表格范围,选择 “条件格式”,选择 “新建规则” (2)选择 “使用公式确定要设置格式的单元格”,录入公式,选择 “ 格式”,注意: 公式为:=$H1="待解决" ...

  8. python3中文输出乱码的问题

    最近使用you-get这个工具下载视频,发现命令行窗口里显示的媒体标题是乱码(但文件管理器里显示正常).我的命令行窗口的code page是936,sys.stdout.encoding是utf-8, ...

  9. 实践案例丨教你一键构建部署发布前端和Node.js服务

    如何使用华为云服务一键构建部署发布前端和Node.js服务 构建部署,一直是一个很繁琐的过程 作为开发,最害怕遇到版本发布,特别是前.后端一起上线发布,项目又特别多的时候. 例如你有10个项目,前后端 ...

  10. xpath和css选择器对比

    基本语法对比 都可以在html中提取内容,但xpath可以提取xml的内容.