花盆

题目传送门

单调队列的题目还是滑动窗口最典型,如果猜到可以用单调队列解题,看看能否通过什么方法转化成滑动窗口(如二分答案)。单调队列的题一般涉及最大值和最小值的问题,好像有的题目也可以用ST表解决。
这道题因为知道是单调队列才做的(硬上单调队列...)
简化题意:找到(x)坐标相差最近的两个点,使它们的纵坐标差值大于等于(D)
(先将点按照(x)由小到大排序)
最开始两个思路:

思路一:枚举每个点,把它当做低的那个点(这样答案一定会被枚举到)。这就要找它左边比它高的点(右边的情况倒着再来一遍)。维护一个单调递减的序列,因为(x)使有序的,所以单调队列中越靠左的点里当前点越远,并且越高。因此二分出(或者直接枚举)一个符合条件的(y),用它和当前点的距离更新答案。
思路二:二分一个答案(L),还是枚举每个点,把它当做低的那个点(这样答案一定会被枚举到)。这就要找它左边比它高的点(右边的情况倒着再来一遍)。维护一个单调递减的序列,因为(x)使有序的,所以单调队列中越靠左的点里当前点越远,并且越高。所以二分出(x)据当前点小于等于(L)的单调队列中最左边的点,只要它们两个的纵坐标的差值大于等于(D),就(return) (true)。如果一直没有(return),那么就不可行。

只写了思路二的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mid ((l+r)>>1)
#define midd ((ll+rr)>>1)
using namespace std;
const LL N = 100005;
LL n,D,maxx,minn=1000006,l=1,r;
LL maxxl,minnl=1000006,ans;
struct node{
	LL x,y;
	bool friend operator < (const node& a,const node& b)
	{ return a.x<b.x; }
}a[N],q[N];
LL aaa[40000000];
inline int read()
{
	int ans=0,w=1;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-') { w=-1; c=getchar(); }
	while(c>='0'&&c<='9')
	{ ans=ans*10+c-'0'; c=getchar(); }
	return ans*w;
}
bool check(LL u)
{
	l=1,r=0;
	for(LL i=1;i<=n;i++)
	{
		while(q[r].y<=a[i].y&&r) --r;
		LL ll=1,rr=r,ansp=-1;
		while(ll<=rr)
		{
			if(a[i].x-q[midd].x<=u) ansp=midd,rr=midd-1;
			else ll=midd+1;
		}
		if(q[ansp].y-a[i].y>=D&&ansp>0) return true;
		q[++r].x=a[i].x;
		q[r].y=a[i].y;
	}
	l=1;r=0;
	for(LL i=n;i>=1;i--)
	{
		while(q[r].y<=a[i].y&&r) --r;
		LL ll=1,rr=r,ansp=-1;
		while(ll<=rr)
		{
			if(q[midd].x-a[i].x<=u) ansp=midd,ll=midd+1;
			else rr=midd-1;
		}
		if(q[ansp].y-a[i].y>=D&&ansp>0) return true;
		q[++r].x=a[i].x;
		q[r].y=a[i].y;
	}
	return false;
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
//	cin>>n>>D;
//	scanf("%lld%lld",&n,&D);
	n=read(); D=read();
	for(LL i=1;i<=n;i++)
	{
		scanf("%lld%lld",&a[i].x,&a[i].y);
		minn=min(minn,a[i].y);
		maxx=max(maxx,a[i].y); 
		minnl=min(minnl,a[i].x);
		maxxl=max(maxxl,a[i].x); 
	}
//	cout<<maxx<<"*"<<minn<<endl;
	if(maxx-minn<D) { printf("-1
"); return 0; }
	sort(a+1,a+n+1);
	LL l=1,r=maxxl-minnl;
	ans=r;
	while(l<=r)
	{
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld
",ans);
	return 0;
}

写完另一个算法,又回来改了半天,还是没改对,虽然感觉是和另一个算法一样的错误。

题解思路:二分出一个(L),求从左到右每一个长度为(L)的区间中的最大值和最小值(滑动窗口),看是否有满足条件的。

第一次交的时候90分:
enter image description here
100分:
enter image description here

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1) 
using namespace std;
const int N = 100005;
const int inf = 2147483647;
int n,D,l1=1,r1,l2=1,r2;
int ans,min1=inf,min2=inf,max1,max2;
struct node{
	int x,y;
	// bool friend operator <(const node& a,const node& b)
	// { return a.x<b.x; }
}a[N],q1[N],q2[N];
bool cmp(node a,node b)
{ return a.x<b.x; }
bool check(int u)
{
	l1=l2=1; r1=r2=0; 
	for(int i=1;i<=n;i++)
	{
		while(l1<=r1&&a[i].x-q1[l1].x>u) ++l1;
		while(l2<=r2&&a[i].x-q2[l2].x>u) ++l2;
		while(q1[r1].y<=a[i].y&&r1) --r1;
		while(q2[r2].y>=a[i].y&&r2) --r2;
		q1[++r1].x=a[i].x; q1[r1].y=a[i].y;
		q2[++r2].x=a[i].x; q2[r2].y=a[i].y;
		if(q1[l1].y-q2[l2].y>=D) return true;
	}
	return false;
}
int main()
{
	scanf("%d%d",&n,&D);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		min1=min(min1,a[i].x);
		min2=min(min2,a[i].y);
		max1=max(max1,a[i].x);
		max2=max(max2,a[i].y);
	}
	if(max1-min1<D) { printf("-1
"); return 0 ;}
	sort(a+1,a+n+1,cmp);
	int l=1,r=max1-min1;
	while(l<=r)
	{
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/karryW/p/11374030.html