我们来换种思路。
题目大意
分析
假设我们已经会单调栈了,(如果不会可以去看看其他题解),所以单调栈的思想这里我就不再说了。
考虑使用笛卡尔树解决该问题。
什么是笛卡尔树?
人话翻译如下:
概念:笛卡尔树是一种二叉树,每个节点有两个值\(key\)和\(value\)。
性质:
- 堆的性质。笛卡尔树的树根是这一子树中\(key\)值最小(大)的元素,父节点的键值均小于(或大于)其左右子节点的键值,可以这样递归地弄下去。
- 树的中序遍历即为原数组序列。
如图,这样就是一棵原序列的笛卡尔树。(可以结合之前讲的看一下)
构造笛卡尔树
一般,我们规定按\(key\)为第一关键字排序,(很多时候,\(key\)值即为数组下标)。
用单调栈实时维护当前树中的最右链,模仿递归建树的过程(建虚树也是这种思想啊)。
以维护小根堆为例,在以\(key\)值增序排序后,我们考虑插入一个新点\(i\),它的值为\(V\)。
我们从栈顶出发,一直找到第一个位置\(pos\)的\(value\)小于\(V\),因为是小根堆,所以要保证\(i\)是\(pos\)的儿子,又因为当前的\(key\)值大于之前出现过的所有值,所以\(i\)是\(pos\)的右儿子,需要把原来\(pos\)的右儿子变成i的左儿子,代码注释里也有。
然后\(O(n)\)完成对笛卡尔树的构建。
本题的解决
\(key\)值就是数组下标,无需排序,直接高度建树。
然后直接在树上从根节点往下走,每个节点的高度确定,所能扩展的长度即为它的子节点的区间长度,每走到一个节点就去个\(max\),\(O(n)\)的遍历,总时间复杂度也是\(O(n)\)的(虽然有\(2\)这个常数\(TAT\))。
注意多组数据的话,及时在回溯时初始化。
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define Re register
#define ll long long
const int N = 100000 + 5;
const int INF = 0x7fffffff;
inline int read() {
int res = 0; bool f = 0; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = 1; ch = getchar(); }
while (ch <= '9' && ch >= '0') { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); }
return f ? (~ res + 1) : res;
}
ll ans;
int n, a[N];
inline ll max(ll a, ll b) { return a < b ? b : a; }
struct Cartesian_Tree {
int sta[N], top, ls[N], rs[N], root;
inline void init() {
top = 0;
for (Re int i = 1;i <= n; ++i) {
/* 用栈来维护最右链,一直找到一个位置pos的a值会小于当前a值 */
while (top && a[sta[top]] >= a[i]) ls[i] = sta[top--];
/*
因为是小根堆,所以要保证i是pos的儿子,又因为当前的key值大于之前出现
过的所有值,所以i是pos的右儿子,需要把原来pos的右儿子变成i的左儿子
*/
if (top) rs[sta[top]] = i;
sta[++top] = i;
}
root = sta[1];
}
inline int dfs(int x) {
if (!x) return 0;
int res = dfs(ls[x]) + dfs(rs[x]) + 1;
ls[x] = 0, rs[x] = 0;
ans = max(ans, (ll)res * a[x]);
return res;
}
}CT;
int main(){
while (1) {
n = read();
if (n == 0) break;
for (Re int i = 1;i <= n; ++i) a[i] = read();
CT.init();
ans = 0;
CT.dfs(CT.root);
printf("%lld\n", ans);
}
return 0;
}