bzoj 4653: [Noi2016]区间
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; }
一念起,天涯咫尺; 一念灭,咫尺天涯。