[Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086

Posted Winniechen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086相关的知识,希望对你有一定的参考价值。

额...

首先,看到这道题,第一想法就是二分答案+线段树...

兴高采烈的认为我一定能AC,之后发现n是500000...

nlog^2=80%,亲测可过...

由于答案是求满足题意的最大长度-最小长度最小,那么我们可以考虑将区间按长度排序

之后,因为我们是需要最大最小,所以,我们必定选择在排完序的区间上取连续的一段是最优情况(起码不会比别的差)

因此,考虑双指针扫一下就可以了...

是不是很水?

由于懒得写离散化,一开始写的动态开点线段树,我*****什么鬼?mle?!256mb开不下!

loj+洛谷上95%,附上代码...

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
#define N 500005
#define lson l,m,tr[rt].ls
#define rson m+1,r,tr[rt].rs
#define PushUp(rt) tr[rt].maxx=max(tr[tr[rt].ls].maxx,tr[tr[rt].rs].maxx)
struct no
{
	int ls,rs,maxx,add;
}tr[N*40];
int n,m,ans,cnt;
struct node
{
	int l,r,len;
}a[N];
bool cmp(const node &a,const node &b)
{
	return a.len<b.len;
}
void PushDown(int rt)
{
	if(tr[rt].add)
	{
		if(!tr[rt].ls)tr[rt].ls=++cnt;
		if(!tr[rt].rs)tr[rt].rs=++cnt;
		tr[tr[rt].ls].maxx+=tr[rt].add;
		tr[tr[rt].rs].maxx+=tr[rt].add;
		tr[tr[rt].ls].add+=tr[rt].add;
		tr[tr[rt].rs].add+=tr[rt].add;
		tr[rt].add=0;
	}
}
void Update(int L,int R,bool c,int l,int r,int &rt)
{
	if(!rt)rt=++cnt;
	if(L<=l&&r<=R)
	{
		tr[rt].maxx+=c?1:-1;
		tr[rt].add+=c?1:-1;
		return ;
	}
	PushDown(rt);
	int m=(l+r)>>1;
	if(m>=L)Update(L,R,c,lson);
	if(m<R)Update(L,R,c,rson);
	PushUp(rt);
}
int main()
{
	ans=1<<30;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].len=a[i].r-a[i].l;
	}
	sort(a+1,a+n+1,cmp);
	int l=1,r=0,rot=0;
	while(r<n)
	{
		while(tr[rot].maxx<m&&r<n){r++;Update(a[r].l,a[r].r,1,0,1<<30,rot);}
		if(tr[rot].maxx<m)break;
		while(tr[rot].maxx>=m&&l<n){Update(a[l].l,a[l].r,0,0,1<<30,rot);l++;}
		ans=min(a[r].len-a[l-1].len,ans);
	}
	printf("%d\n",ans==1<<30?-1:ans);
	return 0;
}

  这显然就不能AC,那么我们可以考虑用一下离散化...

离散化后,线段树的空间复杂度从(nlog(1<<30))变成(nlog(n*2))之后,空间就降下来了...

附上AC代码...

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
#define N 500005
#define lson l,m,tr[rt].ls
#define rson m+1,r,tr[rt].rs
#define PushUp(rt) tr[rt].maxx=max(tr[tr[rt].ls].maxx,tr[tr[rt].rs].maxx)
struct no
{
	int ls,rs,maxx,add;
}tr[N*10];
int p[N<<1];
int n,m,ans,cnt;
struct node
{
	int l,r,len;
}a[N];
bool cmp(const node &a,const node &b)
{
	return a.len<b.len;
}
void PushDown(int rt)
{
	if(tr[rt].add)
	{
		if(!tr[rt].ls)tr[rt].ls=++cnt;
		if(!tr[rt].rs)tr[rt].rs=++cnt;
		tr[tr[rt].ls].maxx+=tr[rt].add;
		tr[tr[rt].rs].maxx+=tr[rt].add;
		tr[tr[rt].ls].add+=tr[rt].add;
		tr[tr[rt].rs].add+=tr[rt].add;
		tr[rt].add=0;
	}
}
void Update(int L,int R,bool c,int l,int r,int &rt)
{
	if(!rt)rt=++cnt;
	if(L<=l&&r<=R)
	{
		tr[rt].maxx+=c?1:-1;
		tr[rt].add+=c?1:-1;
		return ;
	}
	PushDown(rt);
	int m=(l+r)>>1;
	if(m>=L)Update(L,R,c,lson);
	if(m<R)Update(L,R,c,rson);
	PushUp(rt);
}
int main()
{
	ans=1<<30;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].len=a[i].r-a[i].l;
		p[(i<<1)-1]=a[i].l;
		p[i<<1]=a[i].r;
	}
	sort(p+1,p+n*2+1);
	for(int i=1;i<=n;i++)
	{
		int x=lower_bound(p+1,p+n*2+1,a[i].l)-p;
		a[i].l=x;
		x=lower_bound(p+1,p+n*2+1,a[i].r)-p;
		a[i].r=x;
	}
	sort(a+1,a+n+1,cmp);
	int l=1,r=0,rot=0;
	while(r<n)
	{
		while(tr[rot].maxx<m&&r<n){r++;Update(a[r].l,a[r].r,1,1,n*2,rot);}
		if(tr[rot].maxx<m)break;
		while(tr[rot].maxx>=m&&l<n){Update(a[l].l,a[l].r,0,1,n*2,rot);l++;}
		ans=min(a[r].len-a[l-1].len,ans);
	}
	printf("%d\n",ans==1<<30?-1:ans);
	return 0;
}

  离散化什么的,用lower_bound就好了,懒得写二分查找了...反正不会tle...

 

以上是关于[Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ4653][Noi2016]区间

bzoj 4653: [Noi2016]区间

BZOJ4653: [Noi2016]区间

bzoj4653 [Noi2016]区间

BZOJ4653[Noi2016]区间 双指针法+线段树

[BZOJ]4653: [Noi2016]区间