[笔记]单调栈
单调栈,顾名思义就是存储在栈中的元素是具有单调性的(单调递增或单调递减),根据不同的要求进行维护
来看看洛谷的板子题.题目要求找第一个比当前数大的数的序号.
一开始我手推了一下,发现了如何实现单调栈,拿样例来看:栈里面先\(push\)进去\(1\),接着读取到了\(4\),发现它比栈顶元素大,就弹出\(1\),加入\(4\),而1对应的答案也就是\(4\),下标为\(2\).接着读取的是\(2,3\),这两个数放入栈中不会破坏单调性,直到读取到\(5\),它比栈里面所有的元素都打,所以栈里面所有元素的答案都是\(5\),栈被清空,栈中加入\(5\),而此时\(5\)是最后一个元素,所以它的答案是\(0\).
这样基本的思路就出来了.在实现的时候我一开始遇到了问题,我最初写的是直接用栈来维护序列的数值,再用一个\(map\)来映射标号,记录答案,但由于\(STL\)等奇奇怪怪的原因导致了\(TLE\).看过题解后我发现其实可以直接用栈来维护下标,在判断的时候将栈内元素作为下标来比较即可,还是我太菜了~~
总体而言,单调栈还是比较简单,只是根据问题的不同会有变化,只做模板题是肯定不够的,还要在以后的练习中灵活变通
代码如下
#include <bits/stdc++.h>
using namespace std;
stack < int > s;
long long a[3000010];
long long ans[3000010];
int main(){
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
}
s.push(1);
for(int i = 2;i <= n;i++){
while(!s.empty() && a[i] > a[s.top()]){
ans[s.top()] = i;
s.pop();
}
s.push(i);
}
for(int i = 1;i < n;i++){
printf("%lld ",ans[i]);
}
printf("0\n");
return 0;
}
校内OJ bzoj4237
题目描述
要统计所有不包含其他点的矩形个数。
暴力算法\(n^3\)显然不行。
考虑通过排序以及分治使得序列变得有序从而加快速度。
首先按照\(x\)维度排序。然后通过分治,将一个区间分成两部分,分别维护两个单调栈(每次都要)。
构成答案的左下角点在左半部分,右上角的点在右半部分(做到不重不漏)。统计答案只需根据左半部分的每一个点通过二分查找右半部分符合的个数即可。(右半部分的所有点x坐标都比左边大,y又是经过有序维护)
复杂度
\(O(n\log^2n)\)
代码
#include<bits/stdc++.h>
using namespace std;
struct coord{
int x,y;
}c[200010];
int n,que1[200010],que2[200010];
long long ans;
bool cmp1(coord A,coord B){
return A.x < B.x;
}
bool cmp2(coord A,coord B){
return A.y > B.y;
}
void CDQ(int l,int r){//每次二分
if(l == r)return;
int mid = (l + r) / 2;
CDQ(l,mid);
CDQ(mid + 1,r);
sort(c + l,c + mid + 1,cmp2);//按照y的降序排列
sort(c + mid + 1,c + r + 1,cmp2);
int pos = mid + 1,top1 = 0,top2 = 0;//top1是que1的下标,维护右半部分
for(int i = l;i <= mid;i++){//遍历左半部分的每一个累加答案
while(pos <= r && c[pos].y >= c[i].y){//更新右边单调栈,按照y的递减顺序
while(top1 > 0 && c[pos].x < c[que1[top1]].x)
top1--;
que1[++top1] = pos;
pos++;
}
while(top2 > 0 && c[i].x > c[que2[top2]].x)
top2--;
if(top2 > 0){//因为单调栈内有序,通过二分找到符合条件的下标
int find_l = 1,find_r = top1;
while(find_l <= find_r){
int mmid = (find_l + find_r) / 2;
if(c[que1[mmid]].y > c[que2[top2]].y)//所有左边符合条件
find_l = mmid + 1;
else
find_r = mmid - 1;
}
ans += top1 - find_l + 1;
}
else ans += top1;
que2[++top2] = i;//处理完一个加入单调栈
}
return;
}
int main(){
int n;
cin>>n;
for(int i = 1;i <= n;i++){
cin>>c[i].x>>c[i].y;
}
sort(c + 1,c + n + 1,cmp1);//先按照排序保证二分的有序
CDQ(1,n);
cout<<ans<<endl;
}