题解 P1712 【区间】

上进的z君

2016-09-15 18:17:07

Solution

首先分析一下题目,l,r这么大很显然就是要离散化了。 接着我们又注意到题目要求的答案跟“最大”,“最小”有关,所以我们就会联想到单调性。 既然是跟区间长度有关,那我们不妨就先按区间长度排个序好了,反正这样子并不会影响答案。 然后我们思考一种最朴素的做法,那就是按排序后的顺序逐一加入区间,然后看看是否有 一个点的被覆盖次数>=m。 如果有的话那就统计一下答案,然后将前面加入的按顺序删掉,直到<m。 重复上诉的过程。 很显然这利用了尺取法的思想。 那么问题就是我们如何快速地得知是否有 一个点的被覆盖次数>=m。 那就很显然维护一棵线段树就好了。 于是这题就成功解决。 ```cpp #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<stack> #include<queue> using namespace std; const int maxn=501000; const int INF=1e9; struct Point{ int val,ord; }p[maxn*4]; struct Data{ int len,ord; }a[maxn*4]; int L[maxn*2],R[maxn*2],n,m,Right,cur=0; int tree[maxn*8],add[maxn*8]; bool Cmp1(Point x1,Point x2){ if(x1.val<x2.val)return true; return false; } bool Cmp2(Data x1,Data x2){ if(x1.len<x2.len)return true; return false; } void Down(int rt,int l,int r){ if(!add[rt])return; int ls=rt*2,rs=rt*2+1; tree[ls]+=add[rt]; tree[rs]+=add[rt]; add[ls]+=add[rt]; add[rs]+=add[rt]; add[rt]=0; return; } void Update(int rt,int l,int r,int x,int y,int val){ if(x>r||y<l)return; if(x<=l&&y>=r){ tree[rt]+=val; add[rt]+=val; return; } int mid=(l+r)/2; Down(rt,l,r); Update(rt*2,l,mid,x,y,val); Update(rt*2+1,mid+1,r,x,y,val); tree[rt]=max(tree[rt*2],tree[rt*2+1]); return; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ int u,v; scanf("%d%d",&u,&v); a[i].len=v-u; a[i].ord=i; cur++; p[cur].val=u; p[cur].ord=i; cur++; p[cur].val=v; p[cur].ord=i; } sort(p+1,p+cur+1,Cmp1); int num=0; p[0].val=-1; for(int i=1;i<=cur;i++){ if(p[i].val!=p[i-1].val) num++; int u=p[i].ord; if(!L[u]) L[u]=num; else R[u]=num; } Right=num; sort(a+1,a+n+1,Cmp2); int ans=INF,le=0,ri=0; while(true){ while(tree[1]<m&&ri<=n){ ri++; int u=a[ri].ord,v=L[u],w=R[u]; Update(1,1,Right,v,w,1); } if(tree[1]<m)break; while(tree[1]>=m&&le<=n){ le++; int u=a[le].ord,v=L[u],w=R[u]; Update(1,1,Right,v,w,-1); } ans=min(ans,a[ri].len-a[le].len); } if(ans==INF)printf("-1\n"); else printf("%d\n",ans); return 0; } ```