Loading

2022.12.21 总结

今天学的单调栈!!!!!

P5788 【模板】单调栈

洛谷 P5788 【模板】单调栈

题意

求出每个 \(a_i\) 后面第一个大于 \(a_i\) 元素的下标 \(j\),如果没有,则为 0。

思路

首先是暴力,这个很容易,枚举每个数,再把它后面的数都枚举出来,找到就可以了,时间复杂度 \(O(n ^ 2)\)

所以我们可以考虑枚举答案,用栈来实现:

  1. 如果栈不为空,且栈顶元素小于当前元素,就说明当前元素是栈顶元素的答案,弹出栈顶,继续比较。

  2. 否则,压入当前元素。

  • 时间复杂度:

因为每个元素都是进栈出栈一次,所以时间复杂度为 \(O(n)\)。(优秀线性!!!!)

  • 为什么叫单调栈?

因为栈中元素是单调不增的(推样例就可以看出来了!!!)。

代码

#include <iostream>

using namespace std;

const int N = 3e6 + 10;

int n, a[N], s[N], top, ans[N];

int main() {
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    while (top && a[s[top]] < a[i]) {  // 判断是否为空且比较大小
      ans[s[top]] = i;  // 记录答案
      top--;  // 弹出
    }
    s[++top] = i;  // 压入
  }
  for (int i = 1; i <= n; i++) {
    cout << ans[i] << ' ';
  }
  return 0;
}

B3666 求数列所有后缀最大值的位置

B3666 求数列所有后缀最大值的位置

题意

有一个序列 \(a\),初始为空,有 \(n\) 次操作,每次在 \(a\) 的末尾添加一个正整数 \(x\)

每次操作后,请你求出所有后缀最大值的位置的异或和。

后缀最大值:当 \(i\) 后面的所有元素都小于 \(a_i\) 时,\(a_i\) 就是序列的一个后缀最大值。

思路

单调栈模拟。

  1. 当栈不为空且栈顶元素小于等于当前元素时,再次异或栈顶元素(两个相同的数异或结果为 0,0 异或任何数都还是等于那个数),弹出。

  2. 将当前元素压入栈中,并且答案异或当前元素。

栈中元素是单调递增的。

时间复杂度

每个元素进栈出栈一次,\(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n, s[N], top;
unsigned long long a[N], ans;

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  for (int i = 1; i <= n; i++) {
    while (top && a[i] >= a[s[top]]) {
      ans ^= s[top--];  // 取消栈顶元素
    }
    ans ^= i, s[++top] = i;  // 记录答案并压入
    cout << ans << '\n';
  }
  return 0;
}

P2422 良好的感觉

P2422 良好的感觉

题意

请你求出 \(\max _ {1 \le i < j \le n} \{\min _ {i \le k \le j} \{a_k\} \times \sum _ {k = i} ^ j {a_k}\}\)

思路

  1. \(O(n ^ 3)\),暴力枚举区间,再循环求出区间和和区间最小值。

  2. \(O(n ^ 2)\),暴力枚举区间,枚举区间的同时求出区间最小值和区间和。

  3. \(O(n)\),枚举最小值,求出被它所管辖的范围,前缀和实现。

首先,得想清楚这样一件事,如果当前这个数要想成为某个区间的最小值,那么这个区间内就不能有比它更小的数了。

其次,为了让和最大,那么肯定区间中的数越多越好。

所以,思路就很明显了,找出每个数左边和右边的第一个比它要小的数,确定管辖范围,再暴力枚举每一个数,求出最大值。

时间复杂度

每个数进栈出栈一次,\(O(n)\)

求出最大值,\(O(n)\)

总时间复杂度为 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n, a[N], s[N], top, l[N], r[N];
long long sum[N], ans;

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    sum[i] = sum[i - 1] + a[i];  // 前缀和
  }
  for (int i = 1; i <= n; i++) {
    while (top && a[i] <= a[s[top]]) {
      r[s[top--]] = i - 1;  // 右端点
    }
    l[i] = s[top] + 1, s[++top] = i;  // 左端点
  }
  for (int i = 1; i <= n; i++) {
    ans = max(ans, (sum[r[i]] - sum[l[i] - 1]) * a[i]);  // 求答案
  }
  cout << ans;
  return 0;
}
posted @ 2023-03-02 22:47  chengning0909  阅读(59)  评论(0编辑  收藏  举报