把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷1712】[NOI2016] 区间(尺取法+线段树)

点此看题面

  • 给定\(n\)个区间,要求从中选出\(m\)个满足有交,求出选出的最大区间长度减最小区间长度的最小值。
  • \(n\le5\times10^5,m\le2\times10^5\)

二分答案?

看到这种问题第一反应实际上是二分答案。

考虑我们二分一个答案\(x\),并事先把所有区间按照长度排好序。

然后只要枚举最长的区间,同时双指针维护好可选的最短区间满足长度之差不超过\(x\)

现在要判断这些区间是否能选出\(m\)个有交,其实就是要判断是否存在点被至少\(m\)个区间覆盖。

因此,我们把这些区间修改到线段树上,然后就变成一个区间修改、求全局最大值的裸题了。

但这样的做法是会\(TLE\)的,需要考虑优化。

尺取法

应该说是对这类询问最小的满足条件的子区间问题的一个通用解法吧(当然,前提是必须要有单调性)。

仍然是去枚举最长的区间,但我们不二分答案,而是用双指针来维护无法满足要求的最短区间,用当前区间长度和上一个区间(即能满足要求的最长区间)的长度之差来更新答案。

同样可以用线段树维护。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
using namespace std;
int n,m,dc,dv[2*N+5];
struct Il {int l,r;I bool operator < (Con Il& o) Con {return r-l<o.r-o.l;}}s[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
class SegmentTree
{
	private:
		#define PT CI l=1,CI r=dc,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		#define PU() (V[rt]=max(V[rt<<1]+F[rt<<1],V[rt<<1|1]+F[rt<<1|1]))
		int V[N<<3],F[N<<3];
	public:
		I void U(CI L,CI R,CI v,PT)//区间修改(标记永久化)
		{
			if(L<=l&&r<=R) return (void)(F[rt]+=v);RI mid=l+r>>1;
			L<=mid&&(U(L,R,v,LT),0),R>mid&&(U(L,R,v,RT),0),PU();
		}
		I int Q() {return V[1]+F[1];}//全局询问
}S;
int main()
{
	RI i,j;for(read(n,m),i=1;i<=n;++i) read(s[i].l,s[i].r),dv[i]=s[i].l,dv[n+i]=s[i].r;sort(s+1,s+n+1);
	#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
	for(sort(dv+1,dv+2*n+1),dc=unique(dv+1,dv+2*n+1)-dv-1,i=1;i<=n;++i) s[i].l=GV(s[i].l),s[i].r=GV(s[i].r);//离散化
	RI t=0,ans=1e9+1;for(i=j=1;i<=n;++i) {S.U(s[i].l,s[i].r,1);//加入当前区间
		W(S.Q()>=m) ans=min(ans,(dv[s[i].r]-dv[s[i].l])-(dv[s[j].r]-dv[s[j].l])),S.U(s[j].l,s[j].r,-1),++j;}//尺取法
	return printf("%d\n",ans<=1e9?ans:-1),0;
}
posted @ 2021-04-08 10:10  TheLostWeak  阅读(38)  评论(0编辑  收藏  举报