单调栈
性质:
- 可以屏蔽无用的数据
例题:
POJ 3250 Bad Hair Day
题意:对于数组中每个值,求它右边第一个大于它的值的位置与当前位置之间的值的数目。
思路:从右往左单调栈,维护值为降序的位置。
如:10 3 7 4 12 2
判断时的栈状态:
- i = 6: empty
- i = 5: empty
- i = 4: 5
- i = 3: 5 4
- i = 2: 5 3
- i = 1: 5 3 2
代码:
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string.h>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<bitset>
#include<sstream>
#include<functional>
#define eqfor(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll;
stack<ll>s;
ll a[80004];
int main(){
int n;
ll ans = 0,x;
scanf("%d",&n);
eqfor(i,1,n) scanf("%d",&a[i]);
for(int i=n;i>=1;--i){
while(!s.empty()&&a[i]>a[s.top()]) s.pop();
if(s.empty()) ans += n-i;
else ans += s.top()-1-i;
s.push(i);
}
printf("%lld\n",ans);
}
单调队列
性质:
- 区间最值问题
- 队列性质
例题:
一、求区间最值
手写双头队列
#include<iostream>
#include<stdio.h>
#include<set>
#include<cstdio>
#include<string.h>
#include<cstdlib>
#include<stack>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<bitset>
#include<list>
#include<sstream>
#include<map>
#include<functional>
using namespace std;
const int N=1e6+4;
int a[N],q[N];
void getmin(int n,int k){
int head=0,tail=0;
for(int i=1;i<k;i++){
while(head<=tail&&a[q[tail]]>=a[i]) tail--;
q[++tail]=i;
}
for(int i=k;i<=n;i++){
while(head<=tail&&a[q[tail]]>=a[i]) tail--;
q[++tail]=i;
if(q[head]<=i-k) head++;
//到k才是第一组。
//此时,i-k=0
//如果q[head]头的数字的位置位于a中的0以及之前则加。
//a自1开始,q自0开始
printf("%d ",a[q[head]]);
}
}
void getmax(int n,int k){
int head=0,tail=0;
for(int i=1;i<k;i++){
while(head<=tail&&a[q[tail]]<=a[i]) tail--;
q[++tail]=i;
}
for(int i=k;i<=n;i++){
while(head<=tail&&a[q[tail]]<=a[i]) tail--;
q[++tail]=i;
if(q[head]<=i-k) head++;
printf("%d ",a[q[head]]);
}
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
getmin(n,k);
printf("\n");
getmax(n,k);
printf("\n");
}
二、前缀和+单调队列
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+4;
const int inf=1e9;
int s[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int tmp;
scanf("%d",&tmp);
s[i]=s[i-1]+tmp;
}
deque<int>q;
q.push_back(0);
int ans=-inf;
for(int i=1;i<=n;i++){
while(q.size()&&q.front()<i-m)
q.pop_front();
while(q.size()&&s[q.back()]>=s[i])
q.pop_back();
q.push_back(i);
ans=max(ans,s[i]-s[q.front()]);
}
printf("%d\n",ans);
}
练习题目
一、二维单调队列
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+3;
int a[N][N],b[N][N];
//把b数组一开始用作gcd数组就不会MLE,可以优化求lcm的过程
//int gcd[N][N];
//void geta(int n,int m){
// for(int i=1;i<=n;i++)
// for(int j=1;j<=m;j++)
// if(!gcd[i][j])
// for(int k=1;i*k<=n&&j*k<=m;k++){
// gcd[i*k][j*k]=k;a[i*k][j*k]=i*j*k;
// }
//}
int gcd(int a,int b){
if(b==0) return a;
return gcd(b,a%b);
}
int lcm(int a,int b){
return a*b/gcd(a,b);
}
void geta(int n,int m){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=lcm(i,j);
}
void getmax1(int n,int k,int c[],int j){
deque<int>q;
//要想取最大值,必须存递减
for(int i=1;i<k;i++){
while(q.size()&&c[q.back()]<=c[i])
q.pop_back();
q.push_back(i);
}
for(int i=k;i<=n;i++){
while(q.size()&&q.front()<i-k+1)
q.pop_front();
while(q.size()&&c[q.back()]<=c[i])
q.pop_back();
q.push_back(i);
b[i-k+1][j]=c[q.front()];
}
}
void getmax2(int n,int k,int c[],int j){
deque<int>q;
for(int i=1;i<k;i++){
while(q.size()&&c[q.back()]<=c[i])
q.pop_back();
q.push_back(i);
}
for(int i=k;i<=n;i++){
while(q.size()&&q.front()<i-k+1)
q.pop_front();
while(q.size()&&c[q.back()]<=c[i])
q.pop_back();
q.push_back(i);
a[i-k+1][j]=c[q.front()];
}
}
ll getsum(int n,int m){
ll sum=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum+=a[i][j];
return sum;
}
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
geta(n,m);
for(int i=1;i<=n;i++)
getmax1(m,k,a[i],i);
for(int i=1;i<=m-k+1;i++)
getmax2(n,k,b[i],i);
printf("%lld\n",getsum(n-k+1,m-k+1));
}