CodeForces 547B - Mike and Feet(单调栈)
题意:给定一个长度为 n (1 ≤ n ≤ 2 × 105) 的序列,求某一固定长度的连续子序列(长度为1 ~ n)中最小元素的最大值。
对于某一元素,找出它为最小值的最大连续长度区间。
1、对于找出的区间长度,直接取相同区间长度中最小值的最大值即可;
2、对于未找出的区间长度,因为无论怎样一定有 长度为 len 的区间的最小值 <= 长度为 len + 1 的区间的最小值,所以不断从长度为 n - 1 的更新值即可,即 ans[len] = max(ans[len], ans[len + 1]); (len从 n - 1 到 1)
找某一元素为最小值的最大连续区间长度,利用单调栈找到某一元素为最小值的最左端和最右端,算出区间长度即可。
1、往某一方向不断延伸,直到找到比自己小的元素(或者直到序列端点)停止;
2、记下此时的元素位置;
3、将这个元素所在位置压入栈(下个元素如果比这个元素大的话,它记录的位置即就是这个元素(因为无法延伸),否则它一定比这个元素之前延伸经过的元素小,那么下次再判断的时候直接从比这个元素还小的元素判断即可)
#include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream> #include<sstream> #include<iterator> #include<algorithm> #include<string> #include<vector> #include<set> #include<map> #include<stack> #include<deque> #include<queue> #include<list> typedef long long ll; typedef unsigned long long llu; const int MAXN = 100 + 10; const int MAXT = 200000 + 10; const int INF = 0x7f7f7f7f; const double pi = acos(-1.0); const double eps = 1e-6; using namespace std; int n, l[MAXT], r[MAXT], a[MAXT], ans[MAXT]; stack<int> s; int main(){ scanf("%d", &n); for(int i = 0; i < n; ++i) scanf("%d", a + i); for(int i = 0; i < n; ++i){ //单调栈找左端点 while(!s.empty() && a[s.top()] >= a[i]) s.pop(); if(s.empty()) l[i] = -1; else l[i] = s.top(); s.push(i); } while(!s.empty()) s.pop(); for(int i = n - 1; i >= 0; --i){ //单调栈找右端点 while(!s.empty() && a[s.top()] >= a[i]) s.pop(); if(s.empty()) r[i] = n; else r[i] = s.top(); s.push(i); } for(int i = 0; i < n; ++i) //将已经取到最大连续长度的元素记入答案 ans[r[i] - l[i] - 1] = max(ans[r[i] - l[i] - 1], a[i]); for(int i = n - 1; i >= 1; --i) //从区间长度长至短,更新ans(长度长的序列可以取到的最小值,长度短的一定可以取到) ans[i] = max(ans[i], ans[i + 1]); for(int i = 1; i <= n; ++i){ if(i != 1) printf(" "); printf("%d", ans[i]); } printf("\n"); return 0; }