[BZOJ4653][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

数据规模及约定

见“输入

题解

首先将区间离散化,然后按照区间原长度排序,接下来对于区间进行滑动窗口扫描,每加入一个区间就进行一次区间加,删除一个区间就进行一次区间减,用线段树维护全局最大值,当最大值大于等于 m 时就可以更新答案了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxi 500010
#define maxn 1000010
#define oo 2147483647

int n, m, num[maxn], cntn;
struct Interval {
	int l, r, len;
	Interval() {}
	Interval(int _1, int _2, int _3): l(_1), r(_2), len(_3) {}
	bool operator < (const Interval& t) const { return len < t.len; }
} ins[maxi];

int addv[maxn<<2], mxv[maxn<<2];
void pushdown(int o, int l, int r) {
	if(l == r || !addv[o]){ addv[o] = 0; return ; }
	int lc = o << 1, rc = lc | 1;
	addv[lc] += addv[o]; mxv[lc] += addv[o];
	addv[rc] += addv[o]; mxv[rc] += addv[o];
	addv[o] = 0;
	return ;
}
void update(int o, int l, int r, int ql, int qr, int v) {
	pushdown(o, l, r);
	if(ql <= l && r <= qr) {
		addv[o] += v; mxv[o] += v;
		return ;
	}
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(ql <= mid) update(lc, l, mid, ql, qr, v);
	if(qr > mid) update(rc, mid + 1, r, ql, qr, v);
	mxv[o] = max(mxv[lc], mxv[rc]);
	return ;
}
int query(int o, int l, int r) {
	pushdown(o, l, r);
	return mxv[o];
}

int main() {
	n = read(); m = read();
	for(int i = 1; i <= n; i++) {
		int l = read(), r = read();
		ins[i] = Interval(l, r, r - l);
		num[++cntn] = l; num[++cntn] = r;
	}

	sort(num + 1, num + cntn + 1);
	cntn = unique(num + 1, num + cntn + 1) - num - 1;
	for(int i = 1; i <= n; i++)
		ins[i].l = lower_bound(num + 1, num + cntn + 1, ins[i].l) - num,
		ins[i].r = lower_bound(num + 1, num + cntn + 1, ins[i].r) - num;
	sort(ins + 1, ins + n + 1);
	int l = 1, r, ans = oo;
	for(r = 1; r <= n; r++) {
		update(1, 1, cntn, ins[r].l, ins[r].r, 1);
		while(l <= r && query(1, 1, cntn) >= m) {
			ans = min(ans, ins[r].len - ins[l].len);
			update(1, 1, cntn, ins[l].l, ins[l].r, -1);
			l++;
		}
	}

	printf("%d\n", ans < oo ? ans : -1);

	return 0;
}
时间: 2024-12-24 19:44:21

[BZOJ4653][Noi2016]区间的相关文章

【BZOJ4653】[Noi2016]区间 双指针法+线段树

[BZOJ4653][Noi2016]区间 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值. 求所有合法方案中最小

【BZOJ-4653】区间 线段树 + 排序 + 离散化

4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 107  Solved: 70[Submit][Status][Discuss] Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选

[Noi2016]区间[离散化+线段树维护+决策单调性]

4653: [Noi2016]区间 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 621  Solved: 329[Submit][Status][Discuss] Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的

[Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086

额... 首先,看到这道题,第一想法就是二分答案+线段树... 兴高采烈的认为我一定能AC,之后发现n是500000... nlog^2=80%,亲测可过... 由于答案是求满足题意的最大长度-最小长度最小,那么我们可以考虑将区间按长度排序 之后,因为我们是需要最大最小,所以,我们必定选择在排完序的区间上取连续的一段是最优情况(起码不会比别的差) 因此,考虑双指针扫一下就可以了... 是不是很水? 由于懒得写离散化,一开始写的动态开点线段树,我*****什么鬼?mle?!256mb开不下! lo

[Noi2016]区间

题目描述 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri?li,即等于它的右端点的值减去左端点的值. 求所有合法方案中最小的花费.如果不存在合法的方案,输出 ?1. 输入输出格式

bzoj 4653: [Noi2016]区间

额,是不是一到了晚上IQ就--: 这个题一开始完全没有思路.(貌似脑子就没动一下) %了一下题解. 大概是决策是有单调性的,因为要去区间长度差最小,所以接排个序,然后扫描右端点,找出满足有点被覆盖m次的最右的左端点就好. 然后判断是不是有覆盖m个点的用线段树维护一下. (23333,吐槽,为什么离散化二分的时候,把左端点搞成0,右端点搞成L就会RE??excuse me?!!) 1 #include<bits/stdc++.h> 2 #define LL long long 3 #define

【uoj222】 NOI2016—区间

http://uoj.ac/problem/222 (题目链接) 题意:有n个区间,当有m个区间有公共部分时,求m个区间长度的最大值与最小值之差的最小值. Solution  线段树+滑动窗口.这道题很好做,可是在考场上就差一点点,我愣是没想出来.  先将区间按长度排序,保证它们的长度是递增的,这样就可以滑动窗口了.将区间的端点离散化后,用线段树维护每个节点被覆盖的次数,记录当前区间被覆盖次数最多的点被覆盖多少次,当次数达到要求是更新答案,将头指针向后移动. 代码: // uoj222 #inc

UOJ222 NOI2016 区间 线段树+FIFO队列

首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询问覆盖次数可以用线段树实现 1 //C++11 code 2 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 7 const int maxN=500005; 8 const int inf

[UOJ #222][NOI2016]区间(线段树)

Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值 求所有合法方案中最小的花费.如果不存在合法的方案,输出 −1. S