【NOI2016】 区间(线段树,尺取法)

题面

题目描述

在数轴上有 n n n 个闭区间从 1 1 1 n n n 编号,第 i i i 个闭区间为 [ l i , r i ] [l_i,r_i] [li,ri]

现在要从中选出 m m m 个区间,使得这 m m m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x x x ,使得对于每一个被选中的区间 [ l i , r i ] [l_i,r_i] [li,ri],都有 l i ≤ x ≤ r i l_i \leq x \leq r_i lixri

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。

区间 [ l i , r i ] [l_i,r_i] [li,ri] 的长度定义为 ( r i − l i ) (r_i-l_i) (rili) ,即等于它的右端点的值减去左端点的值。

求所有合法方案中最小的花费。如果不存在合法的方案,输出 − 1 -1 1

输入格式

第一行包含两个整数,分别代表 n n n m m m

2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行两个整数表示一个区间,第 ( i + 1 ) (i + 1) (i+1) 行的整数 l i , r i l_i, r_i li,ri 分别代表第 i i i 个区间的左右端点。

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

6 3
3 5
1 2
3 4
2 2
1 5
1 4

样例输出 #1

2

提示

样例输入输出 1 解释

数据规模与约定

本题共 20 个测试点,各测试点信息如下表。

测试点编号 n = n= n= m = m= m= l i , r i l_i,r_i li,ri
1 20 20 20 9 9 9 0 ≤ l i ≤ r i ≤ 100 0 \le l_i \le r_i \le 100 0liri100
2 20 20 20 10 10 10 0 ≤ l i ≤ r i ≤ 100 0 \le l_i \le r_i \le 100 0liri100
3 199 199 199 3 3 3 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
4 200 200 200 3 3 3 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
5 1000 1000 1000 2 2 2 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
6 2000 2000 2000 2 2 2 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
7 199 199 199 60 60 60 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0liri5000
8 200 200 200 50 50 50 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0liri5000
9 200 200 200 50 50 50 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109
10 1999 1999 1999 500 500 500 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0liri5000
11 2000 2000 2000 400 400 400 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0liri5000
12 2000 2000 2000 500 500 500 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109
13 30000 30000 30000 2000 2000 2000 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
14 40000 40000 40000 1000 1000 1000 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
15 50000 50000 50000 15000 15000 15000 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
16 100000 100000 100000 20000 20000 20000 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0liri100000
17 200000 200000 200000 20000 20000 20000 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109
18 300000 300000 300000 50000 50000 50000 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109
19 400000 400000 400000 90000 90000 90000 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109
20 500000 500000 500000 200000 200000 200000 0 ≤ l i ≤ r i ≤ 1 0 9 0 \le l_i \le r_i \le 10^9 0liri109

对于全部的测试点,保证 1 ≤ m ≤ n 1 \leq m \leq n 1mn 1 ≤ n ≤ 5 × 1 0 5 1 \leq n \leq 5 \times 10^5 1n5×105 1 ≤ m ≤ 2 × 1 0 5 1 \leq m \leq 2 \times 10^5 1m2×105 0 ≤ l i ≤ r i ≤ 1 0 9 0 \leq l_i \leq r_i \leq 10^9 0liri109

题解

我们可以将区间离散化,方便我们解决区间交。然后将区间按大小排序(不妨从小到大)。

这样,每个区间 i i i 存在一个最大的 p [ i ] < i p[i]<i p[i]<i 满足存在一个位置被 p [ i ] , i p[i],i p[i],i 之间的区间覆盖不小于 m m m 次。对答案贡献 l e n g t h [ i ] − l e n g t h [ p [ i ] ] length[i]-length[p[i]] length[i]length[p[i]] 。可以 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的复杂度用线段树+扫描线求出某一个 p [ i ] p[i] p[i] 。这个不用多说了吧,就是从左到右删区间,满足线段树中出现次数最大值 ≤ m \leq m m 的最后一个位置。

但是,这个 p [ i ] p[i] p[i] 并不存在单调性。于是不会做了 /kk

在瞥了一眼题解前几行后,三个字在我脑海中炸响:尺取法!

我就像被沉睡的拳击馆魔像轰了一拳……

如果前面存在一个 p [ i ] , i p[i],i p[i],i ,后面的 p [ j ] < p [ i ] ( i < j ) p[j]<p[i](i<j) p[j]<p[i](i<j) ,那么 j j j 就是个废物,这时候,哪怕我们假定 p [ j ] = p [ i ] p[j]=p[i] p[j]=p[i] j j j 也不会更新答案

尺取法就是利用这一点,强制将 p [ i ] p[i] p[i] 变得单调不降。

那么就可以类似 t w o   p o i n t e r s two~pointers two pointers 的操作,总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast")
using namespace std;
#define MAXN 1000005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PQ pair<queue<int>,queue<int> >
#define SQ 320
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
map<int,int> mp;
int l[MAXN],r[MAXN];
int id[MAXN],cn;
int tre[MAXN<<2],lz[MAXN<<2],M;
void maketree(int n) {M=1;while(M<n+2)M<<=1;}
void addtree(int l,int r,int y) {
	for(int s=M+l-1,t=M+r+1;s||t;s >>= 1,t >>= 1) {
		if(s<M) tre[s] = max(tre[s<<1],tre[s<<1|1]) + lz[s];
		if(t<M) tre[t] = max(tre[t<<1],tre[t<<1|1]) + lz[t];
		if((s>>1) ^ (t>>1)) {
			if(!(s&1)) tre[s^1] += y,lz[s^1] += y;
			if(t & 1) tre[t^1] += y,lz[t^1] += y;
		}
	} return ;
}
int main() {
	n = read(); m = read();
	for(int i = 1;i <= n;i ++) {
		l[i] = read();r[i] = read();
		mp[l[i]] = mp[r[i]] = 1; id[i] = i;
	}
	sort(id + 1,id + 1 + n,[](int a,int b){
		return r[a]-l[a] < r[b]-l[b];
	});
	for(auto i = mp.begin();i != mp.end();i ++) {
		i->SE = ++ cn;
	}
	maketree(cn);
	int lp = 1,ans = 0x3f3f3f3f;
	for(int i = 1;i <= n;i ++) {
		int x = id[i];
		addtree(mp[l[x]],mp[r[x]],1);
		if(tre[1] >= m) {
			while(tre[1] >= m) {
				addtree(mp[l[id[lp]]],mp[r[id[lp]]],-1);
				lp ++;
			} lp --;
			addtree(mp[l[id[lp]]],mp[r[id[lp]]],1);
		}
		if(tre[1] >= m) ans = min(ans,r[x]-l[x] - (r[id[lp]]-l[id[lp]]));
	}
	if(ans >= 0x3f3f3f3f) ans = -1;
	AIput(ans,'\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值