[NOI2016]区间(线段树+尺取法)
[NOI2016]区间(线段树+尺取法)
题面
在数轴上有n个闭区间 。现在要从中选出 m个区间,使得这m个区间共同包含至少一个位置.对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。求所有合法方案中最小的花费。如果不存在合法的方案,输出-1 。
分析
看到长度最大最小值的差,考虑按长度排序。然后类似尺取法的思路,从小到大不断加入区间,直到存在一个位置被覆盖了至少\(m\)次,此时最后一个加入的区间就是当前条件下最大的区间长度。但此时最短区间长度还可以增加,所以不断从头删除区间,保证存在一个位置被覆盖了至少\(m\)次,并更新答案。区间离散化后用线段树维护区间覆盖次数。
正确性证明: 我们只需要证左端点随右端点单调增加即可。假设一个合法的解的区间是\([x,y]\). 假设右端点\(>y\)后,左端点\(p\)比\(x\)小,那么会在\([p,y]\)找到更优的解,矛盾
时间复杂度:\(O(n\log n)\)
代码
//[NOI2016]区间
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
#define maxn 500000
using namespace std;
struct segment_tree{
struct node{
int l;
int r;
int val;
int mark;
int len(){
return r-l+1;
}
}tree[maxn*2*4+5];
void push_up(int pos){
tree[pos].val=max(tree[pos<<1].val,tree[pos<<1|1].val);//找到最大的覆盖次数
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
push_up(pos);
}
void push_down(int pos){
if(tree[pos].mark){
tree[pos<<1].val+=tree[pos].mark;
tree[pos<<1].mark+=tree[pos].mark;
tree[pos<<1|1].val+=tree[pos].mark;
tree[pos<<1|1].mark+=tree[pos].mark;
tree[pos].mark=0;
}
}
void update(int L,int R,int val,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
tree[pos].val+=val;
tree[pos].mark+=val;
return;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid) update(L,R,val,pos<<1);
if(R>mid) update(L,R,val,pos<<1|1);
push_up(pos);
}
int query_all(){
return tree[1].val;
}
}T;
int n,m;
struct seg{
int len;
int l;
int r;
friend bool operator < (seg p,seg q){
return p.len<q.len;
}
}a[maxn+5];
int tmp[maxn*2+5];
int dn=0;
int main(){
#ifndef LOCAL
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
#endif
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d %d",&a[i].l,&a[i].r);
tmp[++dn]=a[i].l;
tmp[++dn]=a[i].r;
a[i].len=a[i].r-a[i].l+1;
}
sort(tmp+1,tmp+1+dn);
dn=unique(tmp+1,tmp+1+dn)-tmp-1;
for(int i=1;i<=n;i++){
a[i].l=lower_bound(tmp+1,tmp+1+dn,a[i].l)-tmp;
a[i].r=lower_bound(tmp+1,tmp+1+dn,a[i].r)-tmp;
}
sort(a+1,a+1+n);
T.build(1,dn,1);
int ans=INF;
for(int i=1,j=1;i<=n;i++){
T.update(a[i].l,a[i].r,1,1);
while(T.query_all()>=m){
ans=min(ans,a[i].len-a[j].len);
T.update(a[j].l,a[j].r,-1,1);
j++;
}
}
if(ans==INF) printf("-1\n");
else printf("%d\n",ans);
}
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢