入门理解
应用
求某元素向右最远的比它小的元素位置
解题思路
单调递增栈(从栈顶到栈底单调递增)。
注意事项
n s [ n ] ns[n] ns[n] 设置一个最大的数,用于最后弹出所有数。
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
//#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define mp make_pair
#define lowbit(x) ((x) & (-x))
#define ls rt << 1
#define rs rt << 1
#define M ((l + r) >> 1)
const int INT_M = 0x3f3f3f3f;
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1e5 + 10;
const LL INF = 1e18;
int readint() {
int k;
scanf("%d", &k);
return k;
}
int ns[maxn], r[maxn];
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int n;
LL sum = 0;
scanf("%d", &n);
_for(i, 0, n) ns[i] = readint();
ns[n] = INT_M; //设置一个最大的数,用于最后弹出所有数
stack<int> st; //构造单调递增栈
_rep(i, 0, n) {
while (!st.empty() && ns[i] >= ns[st.top()]) {
r[st.top()] = i;
sum += i - st.top() - 1;
st.pop();
}
st.push(i);
}
printf("%lld", sum);
return 0;
}
求子序列中的最小值乘子序列的长度最大
解题思路
单调递减栈同时求出 n s [ i ] ns[i] ns[i] 右边第一个比它小的元素位置 r r r ,以及左边第一个比它小的元素位置 l l l 。
上面两个位置选择一个进行 + 1 +1 +1 或 − 1 -1 −1 操作,方便求解面积。
本题选择维护直接存 r r r 的数组 r [ i ] r[i] r[i](相当于最后左右要求两边大于 n s [ i ] ns[i] ns[i] 的最远端点相减后 + 1 +1 +1 ),以及将 l + 1 l + 1 l+1 得到的数组 l [ i ] l[i] l[i],其含义为:左边大于 n s [ i ] ns[i] ns[i] 的数能延伸的最远位置~
参考代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define mp make_pair
#define lowbit(x) ((x) & (-x))
#define ls rt << 1
#define rs rt << 1
#define M ((l + r) >> 1)
const int INT_M = 0x3f3f3f3f;
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1e5 + 10;
const LL INF = 1e18;
int readint() {
int k;
scanf("%d", &k);
return k;
}
int ns[maxn], r[maxn], l[maxn];
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int n;
while (~scanf("%d", &n) && n) {
LL ans = -1;
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
_for(i, 0, n) ns[i] = readint();
ns[n] = -1; //设置一个最小的数,用于最后弹出所有数
stack<int> st; //构造单调递减栈
_rep(i, 0, n) {
while (!st.empty() && ns[i] < ns[st.top()]) {
r[st.top()] = i;
st.pop();
}
l[i] = st.size() == 0 ? 0 : st.top() + 1;
st.push(i);
}
_for(i, 0, n) {
if (r[i] == 0) r[i] = n;
ans = max(ans, 1LL * (r[i] - l[i]) * ns[i]);
}
printf("%lld\n", ans);
}
return 0;
}
上面一题的拓展:POJ 3494
解题思路
先进行预处理,每一行的某个元素赋值为紧邻的连续 1 1 1 的个数,再对每一行做上面的单调递减栈操作即可。
注意事项
1.用stack
会超时,用数组模拟过了。
int st[maxn];
int t = 0;
_rep(i, 0, m) {
while (t && a[i] < a[st[t - 1]]) {
r[st[t - 1]] = i;
t--;
}
l[i] = t == 0 ? 0 : st[t - 1] + 1;
st[t++] = i;
}
后来发现是因为处理每一行都开了一个 stack
…
不要重复开很多个,写在里面循环会TLE。
代码(TLE)
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define mp make_pair
#define lowbit(x) ((x) & (-x))
#define ls rt << 1
#define rs rt << 1
#define M ((l + r) >> 1)
const int INT_M = 0x3f3f3f3f;
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int maxn = 2010;
const LL INF = 1e18;
int readint() {
int k; scanf("%d", &k); return k;
}
int ns[maxn], r[maxn], l[maxn];
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int n, m;
while (~scanf("%d%d", &n, &m)) {
LL ans = 0;
memset(ns, 0, sizeof(ns));
ns[m] = -1; //保证最后能把所有元素弹出
_for(j, 0, n) {
_for(i, 0, m) ns[i] = readint() ? ns[i] + 1 : 0;
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
stack<int> st; //构造单调递减栈
_rep(i, 0, m) {
while (!st.empty() && ns[i] <= ns[st.top()]) {
//这里用<=,否则TLE?
r[st.top()] = i;
st.pop();
}
l[i] = st.empty() ? 0 : st.top() + 1;
st.push(i);
}
_for(i, 0, m) {
ans = max(ans, 1LL * (r[i] - l[i]) * ns[i]);
}
}
printf("%lld\n", ans);
}
return 0;
}
参考代码(使用stack)
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define mp make_pair
#define lowbit(x) ((x) & (-x))
#define ls rt << 1
#define rs rt << 1
#define M ((l + r) >> 1)
const int INT_M = 0x3f3f3f3f;
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 2010;
const LL INF = 1e18;
int readint() {
int k;
scanf("%d", &k);
return k;
}
int ns[maxn], a[maxn], r[maxn], l[maxn];
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int n, m;
stack<int> st; //构造单调递减栈,注意不要重复开很多个,写在里面循环会TLE
while (~scanf("%d%d", &n, &m)) {
LL ans = 0;
memset(ns, 0, sizeof(ns));
a[m] = -1;
_for(j, 0, n) {
while (!st.empty()) st.pop();
_for(i, 0, m) {
int k = readint();
if (k) ns[i] = ns[i] + 1;
else ns[i] = 0;
a[i] = ns[i];
}
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
_rep(i, 0, m) {
while (!st.empty() && a[i] < a[st.top()]) {
r[st.top()] = i;
st.pop();
}
l[i] = st.empty() ? 0 : st.top() + 1;
st.push(i);
}
_for(i, 0, m) {
if (r[i] == 0) r[i] = m;
ans = max(ans, 1LL * (r[i] - l[i]) * ns[i]);
}
}
printf("%lld\n", ans);
}
return 0;
}
求每种长度子序列中的最大值的最小值
解题思路
对数 a s [ i ] as[i] as[i],求其是某个子序列的最大值的这个子序列长度。
即要求向右、向左走比 a s [ i ] as[i] as[i] 小的数延伸的最远距离,那么就采取类似上面的思想,求 r [ i ] r[i] r[i] 和 l [ i ] l[i] l[i] (只不过这里是单调递增栈),然后去维护每个子序列长度对应的最大值,每次都取最小值。
注意事项
1.最后记得对结果处理一下,可能有的长度没有值,那么就继承比其长的长度的值。
参考代码
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
const int maxn = 1e5 + 10;
const int INT_M = 0x3f3f3f3f;
int r[maxn], l[maxn], ans[maxn];
class Solution {
public:
/**
* 找到所有长度子数组中最大值的最小值
* @param numbers int整型vector 牛牛给出的数据
* @return int整型vector
*/
vector<int> getMinimums(vector<int>& as) {
// write code here
vector<int> ans_vec;
int n = as.size();
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
as.push_back(INT_M); //设置一个最大的数,用于最后弹出所有数
stack<int> st; //构造单调递减栈
_rep(i, 0, n) {
while (!st.empty() && as[i] >= as[st.top()]) {
r[st.top()] = i;
st.pop();
}
l[i] = st.size() == 0 ? 0 : st.top() + 1;
st.push(i);
}
int len;
fill(ans, ans + maxn, INT_M);
_for(i, 0, n) {
len = r[i] - l[i];
ans[len] = min(ans[len], as[i]);
}
for(int i = n; i >= 2; --i) {
ans[i - 1] = min(ans[i - 1], ans[i]);
}
_rep(i, 1, n) ans_vec.push_back(ans[i]);
return ans_vec;
}
};
子序列中的最小值乘以子序列所有元素和最大
注意事项
1.数组开 l o n g l o n g long\ long long long!!!!!
2.前缀和的维护,如计算从 i i i 到 j j j 的和,即计算 p r e [ j ] − p r e [ i − 1 ] pre[j] - pre[i - 1] pre[j]−pre[i−1]。 p r e [ 0 ] = 0 pre[0] = 0 pre[0]=0 方便处理。
3.本代码中相当于 j = r [ m ] + 1 − 1 j = r[m] + 1 - 1 j=r[m]+1−1, i = l [ m ] + 1 − 1 i = l[m] + 1 - 1 i=l[m]+1−1。
参考代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define mp make_pair
#define lowbit(x) ((x) & (-x))
#define ls rt << 1
#define rs rt << 1
#define M ((l + r) >> 1)
const int INT_M = 0x3f3f3f3f;
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int maxn = 1e5 + 10;
const LL INF = 1e18;
int readint() {
int k; scanf("%d", &k); return k;
}
int ns[maxn], r[maxn], l[maxn];
LL pre[maxn]; //前缀和数组
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int m;
scanf("%d", &m);
LL ans = -1;
memset(ns, 0, sizeof(ns));
memset(pre, 0, sizeof(pre));
ns[m] = -1; //保证最后能把所有元素弹出
pre[0] = 0;
_for(i, 0, m) {
ns[i] = readint();
pre[i + 1] = pre[i] + ns[i];
}
memset(r, 0, sizeof(r));
memset(l, 0, sizeof(l));
stack<int> st; //构造单调递减栈
_rep(i, 0, m) {
while (!st.empty() && ns[i] < ns[st.top()]) {
//这里用<=,否则TLE?
r[st.top()] = i;
st.pop();
}
l[i] = st.empty() ? 0 : st.top() + 1;
st.push(i);
}
int ln = 1, rn = 1;
LL tmp;
_for(i, 0, m) {
r[i] = r[i] ? r[i] : m;
tmp = 1LL * (pre[r[i]] - pre[l[i]]) * ns[i]; //这里注意下标的处理
if (tmp > ans) {
ans = tmp; ln = l[i] + 1; rn = r[i];}
}
printf("%lld\n", ans);
if (!ans) ln = rn = 1;
printf("%d %d", ln, rn);
return 0;
}