bzoj 4653: [Noi2016]区间
Description
在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1.
解题报告:
难得一道做得出的NOI,首先发现那一个相交的点一定可以是区间的某个端点,所以可以离散左右端点,那么问题就简单了,然后仔细推敲,发现可以按区间长度排序,然后不就是尺取法了么?如果有一个点被覆盖的次数>=m我们就移动左指针,不然我们就一直往后走,对于覆盖次数>=m我们就维护线段树区间最大值,然后区间修改维护指针移动即可
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define ls (node<<1)
#define rs (node<<1|1)
using namespace std;
const int N=500005;
int b[N<<1],num=0,n,m,tr[N<<4],mark[N<<4];
struct node{
int l,r,val;
bool operator <(const node &pr)const{return val<pr.val;}
}a[N];
void upd(int node){tr[node]=Max(tr[ls],tr[rs]);}
void pushdown(int node){
if(!mark[node])return ;
int k=mark[node];tr[ls]+=k;tr[rs]+=k;
mark[ls]+=k;mark[rs]+=k;mark[node]=0;
}
void updata(int l,int r,int node,int sa,int se,int ad){
if(l>se || r<sa)return ;
if(sa<=l && r<=se){tr[node]+=ad;mark[node]+=ad;return ;}
int mid=(l+r)>>1;pushdown(node);
updata(l,mid,ls,sa,se,ad);updata(mid+1,r,rs,sa,se,ad);
upd(node);
}
void work()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].l,&a[i].r),a[i].val=a[i].r-a[i].l;
b[++num]=a[i].l;b[++num]=a[i].r;
}
sort(a+1,a+n+1);sort(b+1,b+num+1);
int tot=unique(b+1,b+num+1)-b-1;
for(int i=1;i<=n;i++){
a[i].l=lower_bound(b+1,b+tot+1,a[i].l)-b;
a[i].r=lower_bound(b+1,b+tot+1,a[i].r)-b;
}
int l=1,ans=2e9;
for(int i=1;i<=n;i++){
updata(1,tot,1,a[i].l,a[i].r,1);
while(tr[1]>=m && l<i){
ans=Min(ans,a[i].val-a[l].val);
updata(1,tot,1,a[l].l,a[l].r,-1);
l++;
}
if(tr[1]>=m)ans=Min(ans,a[i].val-a[l].val);
}
if(ans!=2e9)printf("%d\n",ans);
else puts("-1");
}
int main()
{
work();
return 0;
}