《兑命》曰:“当一个比你小的(OIer)比你强时,你就打不过他了”,我不信
其(单调队列和单调栈)此之谓乎!
(向语文老师谢罪
单调队列
滑动窗口
贴一下板子……给出了(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;
}