青年节快乐!
单调栈&单调队列
1.单调栈
- 1.从栈底到栈顶满足单调性的结构。
- 2.“栈+维护单调性”
- 3.单调栈作用:把序列中每个元素放到单调栈中维护就可以快速求出区间每个元素的最大/最小值。
- 性质:元素入栈之前会把栈顶破坏单调性的元素删除,保证元素入栈后栈仍保持单调性。
- 一般使用单调栈的题目具有以下两点:离自己最近(栈的后进先出性质),比自己大/小/高/低.
其实单调队列相关问题(尤其是单调队列DP)可以用RMQ来解。本质就是以更加优秀的复杂度求区间最大最小值(甚至RMQ还可以求更多满足条件的值)
经验之谈:不要用 stack 会被卡卡卡卡常。。。
1.奶牛排队
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <bits/stdc++.h> using namespace std; stack <int> s; int n, x; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &x); while (!s.empty() and s.top() >= x) s.pop(); printf("%d ", (s.empty() ? 0 : s.top())); s.push(x); } }
|
2.B. 牛的视野
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <bits/stdc++.h> using namespace std; stack <long long> s; long long n, x, ans; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &x); while (!s.empty() and s.top() <= x) s.pop(); ans += s.size(); s.push(x); } printf("%d\n",ans); }
|
C.滑动窗口

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <bits/stdc++.h> using namespace std;
const int N = 1e6+10; deque <int> q; int a[N],n,k;
int main(){ cin>>n>>k; for(int i = 1;i <= n;i++) scanf("%d",&a[i]); for(int i = 1;i <= n;i++){ if(!q.empty() and q.front() < i-k+1) q.pop_front(); while(!q.empty() and a[q.back()] >= a[i]){ q.pop_back(); } q.push_back(i); if(i >= k) printf("%d ",a[q.front()]); } puts(""); q.clear(); for(int i = 1;i <= n;i++){ if(!q.empty() and q.front() < i-k+1) q.pop_front(); while(!q.empty() and a[q.back()] <= a[i]){ q.pop_back(); } q.push_back(i); if(i >= k) printf("%d ",a[q.front()]); } }
|
[单调栈]D.最大长方形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
#include <bits/stdc++.h> using namespace std; long long st[1010100], t; long long a[1010100], n; long long loww[1010100], hi[1010100]; int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1; i <= n; i++) { while (t != 0 and a[st[t]] >= a[i]) t--; loww[i] = i - st[t]; st[++t] = i; }t = 0; st[0] = n + 1; for (int i = n; i >= 1; i--) { while (t != 0 and a[st[t]] >= a[i]) t--; hi[i] = st[t] - i; st[++t] = i; }
long long maxx = 0; for (int i = 1; i <= n; i++) {
maxx = max(maxx, a[i] * (hi[i] + loww[i] - 1)); } cout << maxx << endl; }
|
E.最大和
- 这里笔者使用单调队列优化区间DP
- dpi 为以 i 结尾长度为 1∼m 之间的数的和的最大值。
- si 是前缀和。
dpi=max{si−sj}⇔dpi=si−min{sj}(j∈[i−m∼i−1])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <bits/stdc++.h> using namespace std; const int N = 1e6+10,L = 20; int s[N],q[N],h,t,n,m,x;
int main(){ cin>>n>>m; for(int i = 1;i <= n;i++){ cin>>x; s[i] = s[i-1]+x; } int ans = -0x3f3f3f3f; for(int i = 1;i <= n;i++){ if(q[h] < i-m) h++; while(h <= t and s[i] <= s[q[t]]) t--; ans = max(ans,s[i]-s[q[h]]); q[++t] = i; } cout<<ans<<endl; }
|