原题链接:https://www.luogu.com.cn/problem/P1712
区间
题目描述
在数轴上有 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 li≤x≤ri 。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。
区间 [ l i , r i ] [l_i,r_i] [li,ri] 的长度定义为 ( r i − l i ) (r_i-l_i) (ri−li) ,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 − 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
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 0≤li≤ri≤100 |
2 | 20 20 20 | 10 10 10 | 0 ≤ l i ≤ r i ≤ 100 0 \le l_i \le r_i \le 100 0≤li≤ri≤100 |
3 | 19 19 19 | 3 3 3 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
4 | 200 200 200 | 3 3 3 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
5 | 1000 1000 1000 | 2 2 2 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
6 | 2000 2000 2000 | 2 2 2 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
7 | 199 199 199 | 60 60 60 | 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0≤li≤ri≤5000 |
8 | 200 200 200 | 50 50 50 | 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0≤li≤ri≤5000 |
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 0≤li≤ri≤109 |
10 | 1999 1999 1999 | 500 500 500 | 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0≤li≤ri≤5000 |
11 | 2000 2000 2000 | 400 400 400 | 0 ≤ l i ≤ r i ≤ 5000 0 \le l_i \le r_i \le 5000 0≤li≤ri≤5000 |
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 0≤li≤ri≤109 |
13 | 30000 30000 30000 | 2000 2000 2000 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
14 | 40000 40000 40000 | 1000 1000 1000 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
15 | 50000 50000 50000 | 15000 15000 15000 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
16 | 100000 100000 100000 | 20000 20000 20000 | 0 ≤ l i ≤ r i ≤ 100000 0 \le l_i \le r_i \le 100000 0≤li≤ri≤100000 |
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 0≤li≤ri≤109 |
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 0≤li≤ri≤109 |
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 0≤li≤ri≤109 |
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 0≤li≤ri≤109 |
对于全部的测试点,保证 1 ≤ m ≤ n 1 ≤ m ≤ n , 1 ≤ n ≤ 5 × 1 0 5 1 \leq m \leq n1≤m≤n,1 \leq n \leq 5 \times 10^5 1≤m≤n1≤m≤n,1≤n≤5×105, 1 ≤ m ≤ 2 × 1 0 5 1 \leq m \leq 2 \times 10^5 1≤m≤2×105, 0 ≤ l i ≤ r i ≤ 1 0 9 0 \leq l_i \leq r_i \leq 10^9 0≤li≤ri≤109 。
题解
本来的想法是,把所有区间按长度排序,然后二分答案,在不超过二分出的长度的情况下伸缩区间,用线段树维护个支持区间加的最大值,只要有的点权值 ≥ m \ge m ≥m就可行。
由于二分套线段树复杂度大概是 O ( n l o g 2 2 n ) O(nlog_2^2n) O(nlog22n)的,不出意外的 T L E 80 \mathcal{TLE}80 TLE80滚粗了。
仔细思考发现,实际上根本不需要二分,伸缩区间的时候存在全局最大值 ≥ m \ge m ≥m直接去更新答案就好了。
代码
记得横坐标需要离散化,同时判断 − 1 -1 −1的情况,然后线段树要开八倍,因为一个区间会有两个横坐标,需要多乘二。
#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=5e5+5;
struct node{
int le,ri,mx,delta;}tree[M<<3];
struct Inter{
int le,ri,len;}inter[M];
bool cmp(Inter a,Inter b){
return a.len<b.len;};
int n,m,x[M<<1],tot,ans=INT_MAX;
void up(int v){
tree[v].mx=max(tree[ls].mx,tree[rs].mx);}
void build(int v,int le,int ri)
{
tree[v].le=x[le],tree[v].ri=x[ri];
if(le==ri)return;
int mid=le+ri>>1;
build(ls,le,mid);build(rs,mid+1,ri);
}
void push(int v,int delta){
tree[v].mx+=delta,tree[v].delta+=delta;}
void down(int v){
if(tree[v].delta)push(ls,tree[v].delta),push(rs,tree[v].delta),tree[v].delta=0;}
void add(int v,int le,int ri,int delta)
{
if(le<=tree[v].le&&tree[v].ri<=ri){
push(v,delta);return;}
down(v);
if(le<=tree[ls].ri)add(ls,le,ri,delta);
if(tree[rs].le<=ri)add(rs,le,ri,delta);
up(v);
}
void in()
{
scanf("%d%d",&n,&m);
for(int i=1,a,b;i<=n;++i)scanf("%d%d",&a,&b),inter[i]=(Inter){
a,b,b-a},x[++tot]=a,x[++tot]=b;
}
void ac()
{
sort(inter+1,inter+1+n,cmp);
sort(x+1,x+1+tot);
tot=unique(x+1,x+1+tot)-1-x;
build(1,1,tot);
for(int le=1,ri=1;;)
{
for(;tree[1].mx<m&&ri<=n;++ri,add(1,inter[ri].le,inter[ri].ri,1));
if(tree[1].mx<m)break;
for(;tree[1].mx>=m;++le,add(1,inter[le].le,inter[le].ri,-1));
ans=min(ans,inter[ri].len-inter[le].len);
}
printf("%d\n",ans==INT_MAX?-1:ans);
}
int main()
{
in(),ac();
system("pause");
}