Monotone stack
Monotone stack is actually to maintain that the sequence in the stack is monotonous. When the element pressed into the monotone stack will break the monotonicity, the element inside will pop up until the monotonicity can be satisfied when the element is pressed into the stack.
vector<int>v; stack<int>s; for (int i = 1; i <= n; ++ i){ while (!s.empty() && v[s.top()] >= v[i]){ // Is it equal to looking at the topic s.pop(); } s.push(i); }
Example: Luogu P5788
This question is for the third i i After i elements, from i + 1 i+1 i+1 element to n n The first of n elements is greater than a i a_i The subscript of the element of ai ^ reverses the sequence to find the last element larger than it in the elements before this element. Then, using the monotone stack to maintain monotonicity, maintain a decreasing sequence from back to front.
code
#include <bits/stdc++.h> using namespace std; const int LEN = 3e6 + 115; int arr[LEN] = {0, }, ans[LEN] = {0, }, n; stack<int>s; int main(){ #ifndef ONLINE_JUDGE freopen("in.in", "r", stdin); #endif std::ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n; for (int i = 1; i <= n; ++ i){ cin >> arr[i]; } for (int i = n; i > 0; -- i){ while (!s.empty() && arr[s.top()] <= arr[i]){ s.pop(); } if (s.empty()){ ans[i] = 0; }else{ ans[i] = s.top(); } s.push(i); } for (int i = 1; i <= n; ++ i){ cout << ans[i] << ' '; } return 0; }
Monotone queue
Monotone queue is the same as monotone stack. There are monotone sequences in the queue (monotone refers to a user-defined state, such as increasing or non decreasing). Different from ordinary queues, both ends of a monotonous queue can pop up, but the queue entry operation is still carried out in a period.
Example: Luogu P1714
Topic analysis
The topics need to be found are the most in the series m m The maximum value of the sum of m numbers. If there are 0 numbers, the answer is 0. The best way to quickly find the sum of several consecutive numbers is to directly find the prefix sum, which can quickly find the sum of these numbers. Therefore, only maintenance is required m m The minimum of m prefixes and is subtracted from the current prefix and to get the answer.
code
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int LEN = 5e5 + 115; LL arr[LEN] = {0, }, x, n, m, ans = 0; deque<LL>deq; // The deque of stl is used here int main(){ #ifndef ONLINE_JUDGE freopen("in.in", "r", stdin); #endif std::ios::sync_with_stdio(false); cin >> n >> m; for (int i = 1; i <= n; ++ i){ cin >> x; arr[i] = x + arr[i - 1]; } deq.push_back(0); // If you don't eat cake, it will naturally be 0 for (int i = 1; i <= n; ++ i){ while (deq.front() + m < i){ // Ensure that the minimum value is in the range of i-m deq.pop_front(); } ans = max(ans, arr[i] - arr[deq.front()]); // Get the best answer while (!deq.empty() && arr[deq.back()] >= arr[i]){ // When joining the team, we need to maintain its monotony deq.pop_back(); } deq.push_back(i); } cout << ans << endl; return 0; }
Example: Luogu P1725
This is a linear dp, and each step can only start from i i i to [ i + l , i + r ] [i+l, i+r] The grid of [i+l,i+r], then the price of this position must be from the grid that can come [ i − r , i + l ] [i-r, i+l] Transfer from [i − r,i+l], and the dynamic gauge equation is f ( i ) = m a x { f ( j ) ∣ i − r ≤ j ≤ i − l } + c o s t ( i ) f(i) = max\left\{ f(j) \, | \, i - r \le j \le i - l \right\} + cost(i) f(i)=max{f(j) ∣ i − r ≤ j ≤ i − l}+cost(i), so you can write such a code.
#include <bits/stdc++.h> #define endn "\n" using namespace std; typedef long long LL; typedef pair<int, int> PII; const int LEN = 1e4 + 10; LL dp[LEN] = {0, }, n, l, r, x, arr[LEN] = {0, }, ans = -0x7FFFFFFFF; void solve(){ cin >> n >> l >> r; for (int i = 0; i <= n; ++ i){ cin >> arr[i]; } for (int i = l; i <= n; ++ i){ for (int j = i - l; j >= 0 && j >= i - r; -- j){ dp[i] = max(dp[i], dp[j] + arr[i]); } if (i + r >= n){ ans = max(ans, dp[i]); } } cout << ans << endn; } int main(){ #ifndef ONLINE_JUDGE freopen("in.in", "r", stdin); #endif std::ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); solve(); return 0; }
Then you found TLE
But in fact, just look at the enumeration in the above code d p [ j ] + a r r [ i ] dp[j] + arr[i] dp[j]+arr[i], if you can directly find the largest d p [ j ] dp[j] dp[j] isn't it better. Take a closer look. This enumeration is to find the maximum value in the interval, so it's OK to use monotone queue enumeration directly.
Optimizing dp using monotone queue
#include <bits/stdc++.h> #define endn "\n" using namespace std; typedef long long LL; typedef pair<int, int> PII; const int LEN = 1e4 + 10; LL dp[LEN] = {0, }, n, l, r, x, arr[LEN] = {0, }, ans = -0x7FFFFFFFF; deque<LL>deq; void solve(){ cin >> n >> l >> r; for (int i = 0; i <= n; ++ i){ cin >> arr[i]; } for (int i = l; i <= n; ++ i){ while (!deq.empty() && dp[deq.back()] < dp[i - l]){ deq.pop_front(); } deq.push_back(i - l); while (deq.front() < i - r){ deq.pop_front(); } dp[i] = dp[deq.front()] + arr[i]; ans = max(ans, dp[i]); } cout << ans << endn; } int main(){ #ifndef ONLINE_JUDGE freopen("in.in", "r", stdin); #endif std::ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); solve(); return 0; }