【刷题笔记】单调队列与单调栈

《兑命》曰:“当一个比你小的\(OIer\)比你强时,你就打不过他了”,我不信
其(单调队列和单调栈)此之谓乎!
(向语文老师谢罪
一天\((24h)\)一个专题,感觉良好,第二天就感冒了

单调队列

滑动窗口

喜闻乐见的板子时间
我对单调队列运作方式的理解:一个\(dalao\)进队后吊打一切ZZ_zuozhe蒟蒻的故事
然后贴一下板子……给出了\(min,max\)的处理方法

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define MAXN 1000005

ll a[MAXN]={0};

struct QUE
{
	ll v,id;
}Q[MAXN];

ll n,k;
ll front,back;

int main()
{
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	front=1;
	back=0;
	for(int i=1;i<=n;i++)
	{
		if(Q[front].id+k<=i)front++;
		while(back>=front&&Q[back].v>a[i])back--;
		Q[++back].v=a[i];
		Q[back].id=i;
		if(i>=k)printf("%lld ",Q[front].v);
	}
	cout<<endl;
	front=1;
	back=0;
	for(int i=1;i<=n;i++)
	{
		if(Q[front].id+k<=i)front++;
		while(back>=front&&Q[back].v<a[i])back--;
		Q[++back].v=a[i];
		Q[back].id=i;
		if(i>=k)printf("%lld ",Q[front].v);
	}
	return 0;
}

Look Up 仰望

弄清元素退队的过程,这题就很显然,因为每个元素都是被他之后第一个比他大的元素挤出去的,所以这个时候顺便存一下答案就成,感觉还行

	front=1;
	back=0;
	for(int i=1;i<=n;i++)
	{
		while(front<=back&&h[i]>Q[back].v)
		{
			ans[Q[back].id]=i;
			back--;
		}
		Q[++back].v=h[i];
		Q[back].id=i; 
	}

广告印刷

刚才学到,单调队列可以用来找一个元素前/后第一个比他大/小的元素
这个题里,广告肯定是在一个地方贴上了楼顶的,否则向上扩展一格更优
所以枚举每一个高度作为顶端的可能,分别用单调队列求出他左/右能扩展到的最大长度,最后合起来并乘上高度找出最大值即可

	front=1;
	back=0;
	for(int i=1;i<=n+1;i++)
	{
		while(front<=back&&h[i]<=Q[back].v)
		{
			back--;
		}
		temp1[i]=i-Q[back].id;
		Q[++back].v=h[i];
		Q[back].id=i;
	}
	front=1;
	back=0;
	for(int i=n+1;i>=1;i--)
	{
		while(front<=back&&h[i]<=Q[back].v)
		{
			back--;
		}
		temp2[i]=Q[back].id-i;
		Q[++back].v=h[i];
		Q[back].id=i;
	}
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,(temp1[i]+temp2[i]-1)*h[i]);
	}
	printf("%lld",ans);

良好的感觉

明显枚举最不舒服是哪天,然后左右扩展就行
当时拿广告的代码加了个前缀和直接就过了……

理想的正方形

很暴力,直接横纵跑两遍单调队列然后同时维护最大最小值就出来了,也不知道为啥洛谷上是蓝题……码得很乱,就不放了……

单调栈

就是不从队首出列的单调队列。

City Skyline 城市地平线

维护一个单增栈,插入时每当遇到比当前高的就++\(ans\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1005

struct STA
{
	ll w,h;
}S[MAXN];
ll top=0;

ll n,w,x,y;
char ch;

ll h[MAXN][MAXN]={0};

ll ans;

int main()
{
	scanf("%lld%lld",&n,&w);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&h[i][0],&h[i][1]);
	}
//	cout<<endl;
//	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<h[i][j]<<' ';cout<<endl;}
//	cout<<endl;
	ans=0;
	top=0;
	for(int i=1;i<=n+1;i++)
	{
		while(top>0&&h[i][1]<=S[top].h)
		{
			if(h[i][1]!=S[top].h)ans++;
			top--;
		}
		S[++top].h=h[i][1];
	}
	printf("%lld",ans);
	return 0;
}

玉蟾宫

预处理每个点往上能扩展的最大长度,然后每行跑单调栈。
用一个\(temp\)记下被弹出所有元素的宽度,加上初始的\(1\)就是一个元素插进去后最大能向左延伸的宽度。一边跑一边更新答案就行。需要注意的是,为了更新答案,最后要把栈中所有元素都弹出来。记得\(×3\),就完事了

#include<iostream>
using namespace std;
#define ll long long
#define MAXN 1005

ll k[MAXN][MAXN]={0};

struct STA
{
	ll w,h;
}S[MAXN];
ll top=0,tw;

ll n,m;
char ch;

ll h[MAXN][MAXN]={0};

ll ans;

int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%s",&ch);
			if(ch=='R')k[i][j]=0;
			else if(ch=='F')k[i][j]=1;
		}	
	}
	for(int j=1;j<=m;j++)
	{
		h[1][j]=k[1][j];
		for(int i=2;i<=n;i++)
		{
			if(k[i][j])h[i][j]=h[i-1][j]+1;
			else h[i][j]=0;
		}
	}
//	cout<<endl;
//	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<h[i][j]<<' ';cout<<endl;}
//	cout<<endl;
	ans=0;
	for(int i=1;i<=n;i++)
	{
		top=0;
		for(int j=1;j<=m;j++)
		{
			tw=0;
			while(top>0&&h[i][j]<=S[top].h)
			{
				tw+=S[top].w;
				ans=max(ans,S[top].h*tw);
				top--;
			}
			S[++top].h=h[i][j];
			S[top].w=tw+1;
		}
		tw=0;
		while(top>0)
		{
			tw+=S[top].w;
			ans=max(ans,S[top].h*tw);
			top--;
		}
	}
	printf("%lld",ans*3);
	return 0;
}

Blocks

首先明确,这题的答案就是找一个最长的区间,使\(avg \geq k\)
可以把所有元素-=\(k\),然后就变成了找最长的\(sum \geq 0\)的区间
如何找?想到维护个前缀和,那么所有\(i,j\)满足\(pre_i \leq pre_j\)都是合法答案
发现最优的答案只要使左端点尽量靠左,右端点尽量靠右
所以先给左端点弄成单减的(因为尽量靠左,所以这里是\(<S[top]\)的都直接压在栈顶)
然后从最右边开始,一个一个把数往里丢,找到比这个数小的数中最靠左(也就是最大)的一个,更新答案,因为找到的这个点和新枚举的点组成的答案不会更优,所以这个过程中直接\(top\)--就行。
代码很简单,重在思考。

#include<bits/stdc++.h>
#define MAXN 1000005
#define ll long long
using namespace std;

ll a[MAXN]={0};
ll p[MAXN]={0};
ll n,m,t,ans;

struct STA
{
	ll v,id;
}S[MAXN]={0};
ll top=0;

int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);	
	while(m--)
	{
		scanf("%lld",&t);
		for(int i=1;i<=n;i++)p[i]=a[i]+p[i-1]-t;
		top=0;
		ans=0;
		for(int i=1;i<=n;i++)
		{
			if(top==0||p[i]<S[top].v)
			{
				S[++top].v=p[i];
				S[top].id=i;
			}
		}
		for(ll i=n;i>=1;i--)
		{
			if(p[i]>=0)
			{
				ans=max(ans,i);
				continue;
			}
			while(top&&p[i]>=S[top].v)
			{
				ans=max(ans,i-S[top].id);
				top--;
			}
		}
		printf("%lld ",ans);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzzuozhe-gjy/p/12728755.html