题目
题意
在数轴上有 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]$的长度定义为 ( r i − l i ) (r_i-l_i) (ri−li),即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 − 1 -1 −1。
对于全部的测试点,保证 1 ≤ m ≤ n , 1 ≤ n ≤ 5 × 1 0 5 1 \leq m \leq n,1 \leq n \leq 5 \times 10^5 1≤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 。
题解
1.前言
这道题在洛谷上的评级是紫,但我真心觉得没怎么难,可能因为我的方法并不是最优解吧
2.题解
滑动窗口求解:
先把所有区间一个一个的加,直到满足题目的要求
在依然满足要求的情况下,把最前面的区间一个一个的减去(这些区间是没有用的)
重复此过程,直到遍历完毕
注意其中我们如何快速的判断是否满足要求
我们不妨把题目的要求变一下形,要求一个点被至少 m m m条线段覆盖,即是被覆盖 m m m次,如果对于每一条线段,我们把覆盖操作想成是让这个区间内的每一个计数器都加1,知道整个区间内有一个点的计数器大于等于m
那么这就是线段树的板子了
3.代码
需要注意的地方有点多,比如l,r的范围较大,于是我们就要离散化
离散化需要开两倍的数组,所以线段树就要开8倍
时间复杂度 O ( 能 过 ) O(能过) O(能过),可以通过此题
#include<cstdio>
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int MAXN=500005;
struct node{
int l,r;
long long val;
}a[MAXN];
void read(int &x){
char c=getchar();
int t=1;
x=0;
while(c<'0' || c>'9'){
if(c=='-') t=-t;
c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<1)+(x<<3)+(c-'0');
c=getchar();
}
x*=t;
}
bool operator < (node a,node b){
return a.val<b.val;
}
int b[2*MAXN];
int n,m;
int tl;
struct SegmentTree{
int l,r,id;
int maxn;
int lazy;
}Tree[MAXN*8];
void pushup(int id){
Tree[id].maxn=max(Tree[id<<1].maxn,Tree[id<<1|1].maxn);
}
void Spread(int id){
Tree[id<<1].maxn+=Tree[id].lazy;
Tree[id<<1].lazy+=Tree[id].lazy;
Tree[id<<1|1].maxn+=Tree[id].lazy;
Tree[id<<1|1].lazy+=Tree[id].lazy;
Tree[id].lazy=0;
}
void build(int l,int r,int id){
Tree[id].l=l;
Tree[id].r=r;
if(l==r) return;
int mid=l+r>>1;
build(l,mid,id<<1);
build(mid+1,r,id<<1|1);
}
void update(int x,int y,int v,int id){
int l=Tree[id].l,r=Tree[id].r;
if(Tree[id].l>=x && Tree[id].r<=y){
Tree[id].maxn+=v;
Tree[id].lazy+=v;
return;
}
Spread(id);
int mid=(l+r)>>1;
if(x<=mid){
update(x,y,v,id<<1);
}
if(y>mid){
update(x,y,v,id<<1|1);
}
pushup(id);
}
int main(){
read(n);
read(m);
for(int i=1;i<=n;i++){
read(a[i].l);
read(a[i].r);
a[i].val=a[i].r-a[i].l;
b[i*2]=a[i].l,b[i*2+1]=a[i].r;
}
sort(b+1,b+n*2+2);
int M=unique(b+1,b+n*2+1)-(b+1);
int maxn=-1;
for(int i=1;i<=n;i++){
a[i].l=lower_bound(b+1,b+M+1,a[i].l)-b;
a[i].r=lower_bound(b+1,b+M+1,a[i].r)-b;
maxn=max(maxn,max(a[i].l,a[i].r));
}
sort(a+1,a+n+1);
build(1,maxn,1);
int l=0,r=0;
long long ans=0x7f7f7f7f;
while(r<n){
while(Tree[1].maxn<m && r<=n){
r++;
update(a[r].l,a[r].r,1,1);
}
if(Tree[1].maxn<m) break;
while(Tree[1].maxn>=m && l<=r){
l++;
update(a[l].l,a[l].r,-1,1);
}
ans=min(ans,(a[r].val-a[l].val));
}
if(ans==0x7f7f7f7f){
printf("-1");
return 0;
}
printf("%lld",ans);
return 0;
}
/*
5 1
50 30
10 20
30 40
20 10
40 50
*/