单调栈的”一栈两用“。 对数组从左至右维护严格单调递增栈需要不断移除栈顶元素直到栈顶元素小于待入栈元素。在这个过程中待入栈元素是移除元素右侧第一个小于等于自身的元素。在移除过程结束后,栈顶元素是待入栈元素的左侧第一个小于自身的元素。这样一个从左至右严格单调递增栈可以获得每个元素左侧第一个小于自身的元素以及每个元素右侧第一个小于等于自身的元素。
从左至右严格单调递增栈可以获得每个元素左侧第一个小于自身的元素以及每个元素右侧第一个小于等于自身的元素。 从左至右单调非减栈可以获得每个元素左侧第一个小于等于自身的元素以及每个元素右侧第一个小于自身的元素。 从左至右严格单调递减栈可以获得每个元素左侧第一个大于自身的元素以及每个元素右侧第一个大于等于自身的元素。 从左至右单调非增栈可以获得每个元素左侧第一个大于等于自身的元素以及每个元素右侧第一个大于自身的元素。
从左至右维护单调栈可以解决所有需求,然后跟增有关的栈得到左右小于或小于等于自身的值,而减有关的栈得到左右大于/大于等于自身的值。总之在从左至右前提下,增小减大。
int main() {
vector<int> v{1,1,2,3,3,2,1,1,2,2,3};
int n = v.size();
cout << "index "; for (int i=0; i<n; i++) cout << i << " "; cout << endl;
cout << "val "; for (int i:v) cout << i << " "; cout << endl;
vector<int> st;
vector<int> l_g(n, -1), r_ge(n, n);
for (int i=0; i<n; i++) {
while (st.size() && v[st.back()] <= v[i]) {//严减
r_ge[st.back()] = i;
st.pop_back();
}
if(st.size()) l_g[i] = st.back();
st.push_back(i);
}
cout << "l_g "; for (int i:l_g) cout << i << " "; cout << endl;
cout << "r_ge "; for (int i:r_ge) cout << i << " "; cout << endl;
st.clear();
vector<int> l_ge(n, -1), r_g(n, n);
for (int i=0; i<n; i++) {
while (st.size() && v[st.back()] < v[i]) {//非增
r_g[st.back()] = i;
st.pop_back();
}
if(st.size()) l_ge[i] = st.back();
st.push_back(i);
}
cout << "l_ge "; for (int i:l_ge) cout << i << " "; cout << endl;
cout << "r_g "; for (int i:r_g) cout << i << " "; cout << endl;
st.clear();
vector<int> l_l(n, -1), r_le(n, n);
for (int i=0; i<n; i++) {
while (st.size() && v[st.back()] >= v[i]) {//严增
r_le[st.back()] = i;
st.pop_back();
}
if(st.size()) l_l[i] = st.back();
st.push_back(i);
}
cout << "l_l "; for (int i:l_l) cout << i << " "; cout << endl;
cout << "r_le "; for (int i:r_le) cout << i << " "; cout << endl;
st.clear();
vector<int> l_le(n, -1), r_l(n, n);
for (int i=0; i<n; i++) {
while (st.size() && v[st.back()] > v[i]) {//非减
r_l[st.back()] = i;
st.pop_back();
}
if(st.size()) l_le[i] = st.back();
st.push_back(i);
}
cout << "l_le "; for (int i:l_le) cout << i << " "; cout << endl;
cout << "r_l "; for (int i:r_l) cout << i << " "; cout << endl;
return 0;
}
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
val | 1 | 1 | 2 | 3 | 3 | 2 | 1 | 1 | 2 | 2 | 3 |
l_g | -1 | -1 | -1 | -1 | -1 | 4 | 5 | 5 | 4 | 4 | -1 |
r_ge | 1 | 2 | 3 | 4 | 10 | 8 | 7 | 8 | 9 | 10 | 11 |
l_ge | -1 | 0 | -1 | -1 | 3 | 4 | 5 | 6 | 5 | 8 | 4 |
r_g | 2 | 2 | 3 | 11 | 11 | 10 | 8 | 8 | 10 | 10 | 11 |
l_l | -1 | -1 | 1 | 2 | 2 | 1 | -1 | -1 | 7 | 7 | 9 |
r_le | 1 | 6 | 5 | 4 | 5 | 6 | 7 | 11 | 9 | 11 | 11 |
l_le | -1 | 0 | 1 | 2 | 3 | 2 | 1 | 6 | 7 | 8 | 9 |
r_l | 11 | 11 | 6 | 5 | 5 | 6 | 11 | 11 | 11 | 11 | 11 |
//leetcode 1696. 跳跃游戏 VI
for (int i=0; i<n; i++) {
while (que.size() && que.front()+k<i) que.pop_front();
dp[i] = nums[i];
if (que.size()) dp[i] += dp[que.front()];
while (que.size() && dp[que.back()]<dp[i]) que.pop_back();
que.push_back(i);
}
第一个while 保证队列内元素与当前位置距离小于等于k
第二个while移除小于当前待入栈元素保证队列单调非增
如果我们离线处理,发现每个区间$[l,r]$的下一个区间$[l’,r’]$
满足$l\le l’, r \le r’$。那么可以通过单调队列求出最值。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
vector<int> q(n, 0), ans;
int s = 0, e = 0;
for (int i=0; i<nums.size(); i++) {
while (s<e && nums[q[e-1]] < nums[i]) e--;//维护单调递减
q[e++] = i;
while (s<e && k<q[e-1]-q[s]+1) s++; //移除超出范围值。
if (i>=k-1) ans.push_back(nums[q[s]]);
}
return ans;
}
};
满足$r \le r’$。那么可以通过单调栈上二分求出最值。 P3865 【模板】ST 表
#include <bits/stdc++.h>
#define SINGLE_INPUT
#define ull unsigned long long
#define ll long long
#define N 500005
#define MOD 998244353
using namespace std;
template<class t,class u> ostream& operator<<(ostream& os,const pair<t,u>& p) {
return os<<'['<<p.first<<", "<<p.second<<']';
}
template<class t> ostream& operator<<(ostream& os,const vector<t>& v) {
os<<'['; int s = 1;
for(auto e:v) { if (s) s = 0; else os << ", "; os << e; }
return os<<']';
}
template<class t,class u> ostream& operator<<(ostream& os,const map<t,u>& mp){
os<<'{'; int s = 1;
for(auto [x,y]:mp) { if (s) s = 0; else os << ", "; os<<x<<": "<<y; }
return os<<'}';
}
void sol() {
int n, m;
cin >> n >> m;
vector<int> a(n);
for (auto& i:a) cin >> i;
vector<pair<int,int>> p(m);
for (auto& [i,j]:p) {
cin >> i >> j;
i--,j--;
}
// cout << n << " " << m << endl;
// cout << a << " " << p << endl;
vector<int> idx(m);
iota(idx.begin(), idx.end(), 0);
sort(idx.begin(), idx.end(), [&](int x, int y) {
return p[x].second < p[y].second;
});
// cout << idx << endl;
vector<int> ans(m), st;
int u = 0;
for (auto i:idx) {
auto [l,r] = p[i];
while (u<=r) {
while (st.size() && a[st.back()] < a[u]) st.pop_back();
st.push_back(u);
u++;
}
int L = 0, R = st.size();
while (L < R) {
int m = L+R>>1;
if (st[m] >= l) R = m;
else L = m+1;
}
ans[i] = a[st[L]];
// cout << l << " " << r << " " << st << " " << ans[i] << endl;
}
for (auto i:ans) {
cout << i << "\\n";
}
}
int main() {
cout << setprecision(15) << fixed;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifndef SINGLE_INPUT
int t;
cin >> t;
while (t--) {
sol();
}
#else
sol();
#endif
return 0;
}
****