NOI2016 区间 【线段树】
题目
在数轴上有 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)\)
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 1000005,maxm = 500005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
return out * flag;
}
struct node{int l,r;}e[maxm];
int b[maxn],bi,tot = 1,n,m,ans = INF;
int mx[4 * maxn],tag[4 * maxn];
inline bool operator <(const node& a,const node& x){
return (b[a.r] - b[a.l]) < (b[x.r] - b[x.l]);
}
int getn(int x){return lower_bound(b + 1,b + 1 + tot,x) - b;}
void pd(int u){
if (tag[u]){
mx[ls] += tag[u]; mx[rs] += tag[u];
tag[ls] += tag[u]; tag[rs] += tag[u];
tag[u] = 0;
}
}
void modify(int u,int l,int r,int L,int R,int x){
if (l >= L && r <= R){
mx[u] += x; tag[u] += x;
return;
}
pd(u);
int mid = l + r >> 1;
if (mid >= L) modify(ls,l,mid,L,R,x);
if (mid < R) modify(rs,mid + 1,r,L,R,x);
mx[u] = max(mx[ls],mx[rs]);
}
int main(){
n = read(); m = read();
for (int i = 1; i <= n; i++)
b[++bi] = e[i].l = read(),b[++bi] = e[i].r = read();
sort(b + 1,b + 1 + bi);
for (int i = 2; i <= bi; i++) if (b[i] != b[tot]) b[++tot] = b[i];
for (int i = 1; i <= n; i++)
e[i].l = getn(e[i].l),e[i].r = getn(e[i].r);
sort(e + 1,e + 1 + n);
modify(1,1,tot,e[1].l,e[1].r,1);
int l = 1,r = 1,L,R;
L = R = b[e[1].r] - b[e[1].l];
if (mx[1] >= m) ans = min(ans,R - L);
while (r <= n){
while (mx[1] >= m && l < r){
modify(1,1,tot,e[l].l,e[l].r,-1);
l++;
L = b[e[l].r] - b[e[l].l];
if (mx[1] >= m) ans = min(ans,R - L);
}
r++;
if (r > n) break;
R = b[e[r].r] - b[e[r].l];
modify(1,1,tot,e[r].l,e[r].r,1);
if (mx[1] >= m) ans = min(ans,R - L);
}
printf("%d\n",ans == INF ? -1 : ans);
return 0;
}
NOI2016 区间 【线段树】的更多相关文章
- [NOI2016]区间 线段树
[NOI2016]区间 LG传送门 考虑到这题的代价是最长边减最短边,可以先把边按长度排个序,双指针维护一个尺取的过程,如果存在包含某个点的区间数\(\ge m\),就更新答案并把左指针右移,这样做的 ...
- Luogu P1712 [NOI2016]区间(线段树)
P1712 [NOI2016]区间 题意 题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间, ...
- UOJ222 NOI2016 区间 线段树+FIFO队列
首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询 ...
- BZOJ.4653.[NOI2016]区间(线段树)
BZOJ4653 UOJ222 考虑二分.那么我们可以按区间长度从小到大枚举每个区间,对每个区间可以得到一个可用区间长度范围. 我们要求是否存在一个点被这些区间覆盖至少\(m\)次.这可以用线段树区间 ...
- BZOJ4653 [NOI2016]区间 [线段树,离散化]
题目传送门 区间 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就 ...
- BZOJ4653: [Noi2016]区间(线段树 双指针)
题意 题目链接 Sol 按照dls的说法,一般这一类的题有两种思路,一种是枚举一个点\(M\),然后check它能否成为答案.但是对于此题来说好像不好搞 另一种思路是枚举最小的区间长度是多少,这样我们 ...
- 洛谷$P1712\ [NOI2016]$区间 线段树
正解:线段树 解题报告: 传送门$QwQ$ $umm$很久以前做的了来补个题解$QwQ$ 考虑给每个区间按权值($r-l$从大往小排序,依次加入,然后考虑如果有一个位置被覆盖次数等于$m$了就可以把权 ...
- BZOJ4653:[NOI2016]区间(线段树)
Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x ...
- hdu 1540 Tunnel Warfare (区间线段树(模板))
http://acm.hdu.edu.cn/showproblem.php?pid=1540 Tunnel Warfare Time Limit: 4000/2000 MS (Java/Others) ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
随机推荐
- 《队长说得队》【Alpha】Scrum meeting 3
项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十二 团队作业8:软件测试与ALPHA冲刺 团队名称 ...
- Shell脚本调用ftp上传文件
Shell脚本调用ftp上传文件 1.脚本如下 ftp -n<<! open x.x.x.x ###x.x.x.x为ftp地址 user username password ###user ...
- 将unity3d项目嵌入到Android App中使用
创建一个新的AndroidStudio app项目. 1.添加库文件:拷贝unity安装目录下的库文件:Unity\Editor\Data\PlaybackEngines\AndroidPlayer\ ...
- C/C++基础知识:函数指针和指针函数的基本概念
[函数指针] 在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址.可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指 ...
- Git - revert详解
git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销作为一次最新的提交 * git revert HEAD ...
- Linux常用文档操作命令--2
4.文档压缩与解压操作 在Linux中常见的压缩文件有:*.tar.gz.*.tgz.*.gz.*.Z.*bz2等.其每种不同的压缩文件对印的压缩和解压命令也不同. *.tar.gz :tar程序打包 ...
- v2ex站长专访 - 100offer专访Livid:不仅仅是V站站长
转载自: https://www.douban.com/group/topic/121611313/ 前几天上网时偶然发现v2ex站长的blog(https://livid.v2ex.com/),了解 ...
- 怎么删除服务中的mysql服务
可以进WINDOWS的管理里查看MYSQL的服务,把它停止或以DOS下用命令停止1.如果要卸载MYSQL执行下面命令:DOS下>mysqld -remove mysql2.启动MYSQL: DO ...
- Leetcode 75. 颜色分类
题目链接 https://leetcode-cn.com/problems/sort-colors/description/ 题目描述 给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们 ...
- SQL_2_查询Select语句的使用
查询一词在SQL中并不是很恰当,在SQL中查询除了向数据库提出问题之外,还可以实现下面的功能: 1>建立或删除一个表 2>插入.修改.或删除一个行或列 3>用一个特定的命令从几个表中 ...