4653: [Noi2016]区间
Description
在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。
Input
第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9
Output
只有一行,包含一个正整数,即最小花费。
Sample Input
6 3
3 5
1 2
3 4
2 2
1 5
1 4
3 5
1 2
3 4
2 2
1 5
1 4
Sample Output
2
题解:
还是要考虑排序。
我们按长度排序,发现最后选出的线段在一段区间上,把区间上的线段一条一条加到线段树中,最大值肯定>=m
然后枚举左端点,发现最近的右端点不断递增,用指针就好了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=500005;
#define p1 (p<<1)
#define p2 (p<<1|1)
struct node
{
int a,id;
}p[N<<1];
struct Node
{
int l,r,len;
}q[N];
int n,m,i,k,ans,x,t[N<<3],add[N<<3],h[N<<1];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
bool cmp(const node&x,const node&y)
{
return x.a<y.a;
}
bool Cmp(const Node&x,const Node&y)
{
return x.len<y.len;
}
void update(int l,int r,int x,int y,int z,int p)
{
if(x<=l&&r<=y)
{
add[p]+=z;
return;
}
if(add[p]!=0)
{
add[p1]+=add[p];
add[p2]+=add[p];
add[p]=0;
}
int mid=(l+r)>>1;
if(x<=mid) update(l,mid,x,y,z,p1);
if(y>mid) update(mid+1,r,x,y,z,p2);
t[p]=max(t[p1]+add[p1],t[p2]+add[p2]);
}
int main()
{
read(n),read(m);
for(i=1;i<=n*2;i++) p[i].id=i;
for(i=1;i<=n;i++)
read(p[i*2-1].a),read(p[i*2].a),q[i].len=p[i*2].a-p[i*2-1].a;
sort(p+1,p+n*2+1,cmp);
h[p[1].id]=1;k=1;
for(i=2;i<=n*2;i++)
{
if(p[i].a!=p[i-1].a) k++;
h[p[i].id]=k;
}
for(i=1;i<=n;i++) q[i].l=h[i*2-1],q[i].r=h[i*2];
sort(q+1,q+n+1,Cmp);
ans=2e9;
for(i=1;i<=n;i++)
{
while(x<n&&t[1]<m)
{
x++;
update(1,k,q[x].l,q[x].r,1,1);
}
if(t[1]>=m) ans=min(ans,q[x].len-q[i].len);
update(1,k,q[i].l,q[i].r,-1,1);
}
if(ans==2e9) cout<<"-1";else cout<<ans;
return 0;
}