题目描述
在数轴上有NN 个闭区间 [l_1,r_1],[l_2,r_2],...,[l_n,r_n][l1,r1],[l2,r2],...,[ln,rn] 。现在要从中选出MM个区间,使得这MM 个区间共同包含至少一个位置。换句话说,就是使得存在一个 xx ,使得对于每一个被选中的区间[l_i,r_i][li,ri] ,都有 l_i≤x≤r_ili≤x≤ri 。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间[l_i,r_i][li,ri] 的长度定义为r_i-l_iri−li ,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出-1−1 。
输入格式
第一行包含两个正整数N,MN,M 用空格隔开,意义如上文所述。保证1≤M≤N1≤M≤N
接下来NN 行,每行表示一个区间,包含用空格隔开的两个整数l_ili 和r_iri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9N<=500000,M<=200000,0≤li≤ri≤109
输出格式
只有一行,包含一个正整数,即最小花费。
输入输出样例
输入
6 3 3 5 1 2 3 4 2 2 1 5 1 4
输出
2
这道题呢,题目说要用长度最大的区间减去长度最小的区间,正好本来序列就没什么顺序,那我们给区间排个序吧,这样可以方便统计答案。
排完序做什么呢?一定是统计每个点出现的次数吧。对于这些个区间,修改区间上点出现的次数,线段树是常规操作吧。
我们用线段树修改所有点出现的次数的最大值,这样,当枚举到i时,加上区间i对点出现次数的贡献,此时tree[1]如果等于m,这说明一个可能的答案产生了。
我们枚举刚刚统计过的区间j,把他的贡献删掉,线段树区间减。
看看tree[1]是否发生变化。
如果tree[1]变小,那说明这是对这个答案有影响的长度最小的区间了。更新答案ans=min(ans,sec[i].len-sec[j].len)。
想想为什么可以删掉j这个区间?因为i后面的区间一定比i长,减去j后产生的答案一定会更大,因此完全不用考虑j对i后面区间的影响。
既然j被删掉了,那么下一个区间就是j+1了,用head标记这个,当下一个答案产生时,从head开始枚举。
注意到原区间坐标很大,引入离散化是必要的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ls k<<1 #define rs k<<1|1 #define min(x,y) (x<y?x:y) #define max(x,y) (x>y?x:y) using namespace std; const int N=550000; int n,m,ans=0x7fffffff; int temp[N<<4]; int tree[N<<4],tag[N<<4]; struct node{ int l,r,len; }sec[N]; inline bool cmp(node x,node y){ return x.len<y.len; } inline void read(int &x){ x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); } inline void updata(int k,int x,int y){ int mid=x+y>>1; tree[ls]+=tag[k]; tree[rs]+=tag[k]; tag[ls]+=tag[k]; tag[rs]+=tag[k]; tag[k]=0; } inline void change(int k,int x,int y,int l,int r,int val){ if(x>r||y<l) return; if(x>=l&&y<=r){ tree[k]=tree[k]+val; tag[k]+=val; return; } if(tag[k]) updata(k,x,y); int mid=x+y>>1; change(ls,x,mid,l,r,val); change(rs,mid+1,y,l,r,val); tree[k]=max(tree[ls],tree[rs]); } int main() { int i,j,x,y,t; int cnt=0; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ read(sec[i].l),read(sec[i].r); sec[i].len=sec[i].r-sec[i].l; temp[++cnt]=sec[i].l; temp[++cnt]=sec[i].r; } sort(sec+1,sec+n+1,cmp); sort(temp+1,temp+1+cnt); cnt=unique(temp+1,temp+1+cnt)-temp-1; for(i=1;i<=n;i++){ sec[i].l=lower_bound(temp+1,temp+1+cnt,sec[i].l)-temp; sec[i].r=lower_bound(temp+1,temp+1+cnt,sec[i].r)-temp; } int head=0; for(i=1;i<=n;i++){ x=sec[i].l,y=sec[i].r; change(1,1,cnt,x,y,1); if(tree[1]>=m) for(j=head;j<=i;j++){ x=sec[j].l,y=sec[j].r; change(1,1,cnt,x,y,-1);//这时线段树的长度应该是1~cnt,而不是n if(tree[1]<m){ head=j+1; ans=min(ans,sec[i].len-sec[j].len); break; } } } if(ans==0x7fffffff) ans=-1;//不要忘记判断这个。 printf("%d",ans); }