P1712 [NOI2016]区间

我表示我又开始博客了……

题意

非常好理解,自己读题吧

题解

我们可以发现,因为影响答案的只有选取的区间长度的最小值和最大值,所以我们可以考虑先根据区间长度排序,每一次选取区间长度相邻的一段,因为只有最大值最小值会影响答案,所以如果这一段存在一个点被覆盖了 \(m\) 次以上,那么它就可以更新答案。

如何判断是否存在一个点被覆盖了 \(m\) 次,我们可以考虑线段树,并使用类似于尺取法的操作来更新答案,可以证明这样的操作是不会错过正解的。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=500005;
int n,m;
struct Object{int l,r;}s[N];
bool cmp(Object a,Object b){return a.r-a.l<b.r-b.l;};
int h[N<<1],len=0;
map<int,int> mp;
int ans=2e9+7;
struct Node{int data,tag;}tr[N<<3];
void up(int p){tr[p].data=max(tr[p<<1].data,tr[p<<1|1].data);}
void down(int p)
{
	tr[p<<1].data+=tr[p].tag;
	tr[p<<1].tag+=tr[p].tag;
	tr[p<<1|1].data+=tr[p].tag;
	tr[p<<1|1].tag+=tr[p].tag;
	tr[p].tag=0;
	return ;
}
void add(int p,int l,int r,int x,int y,int z)
{
	if(x<=l&&r<=y)
	{
		tr[p].data+=z;
		tr[p].tag+=z;
		return ;
	}
	down(p);
	int mid=(l+r)>>1;
	if(x<=mid)
	add(p<<1,l,mid,x,y,z);
	if(y>=mid+1)
	add(p<<1|1,mid+1,r,x,y,z);
	up(p);
	return ;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	scanf("%d%d",&s[i].l,&s[i].r);
	sort(s+1,s+1+n,cmp);
	for(int i=1;i<=n;++i)
	h[++len]=s[i].l,h[++len]=s[i].r;
	sort(h+1,h+1+len);
	len=unique(h+1,h+1+len)-h-1;
	for(int i=1;i<=len;++i)
	mp[h[i]]=i;
	for(int i=1;i<m;++i)
	add(1,1,len,mp[s[i].l],mp[s[i].r],1);
	int lst=1;
	for(int i=m;i<=n;++i)
	{
		add(1,1,len,mp[s[i].l],mp[s[i].r],1);
		while(tr[1].data>=m)
		ans=min(ans,(s[i].r-s[i].l)-(s[lst].r-s[lst].l)),
		add(1,1,len,mp[s[lst].l],mp[s[lst].r],-1),
		lst++;
	}
	if(ans==2e9+7) printf("-1\n");
	else printf("%d\n",ans);
	return 0;
}
posted @ 2020-08-20 18:37  Point_King  阅读(98)  评论(0编辑  收藏  举报