直方图中最大的矩形(笛卡尔树解决)

喵帕斯!

我们来换种思路。


题目大意

略略略~

分析

假设我们已经会单调栈了,(如果不会可以去看看其他题解),所以单调栈的思想这里我就不再说了。

考虑使用笛卡尔树解决该问题。

什么是笛卡尔树?

笛卡尔树是一种特定的二叉树数据结构,可由数列构造,在范围最值查询、范围top kth查询等问题上有广泛应用。它具有堆的有序性,中序遍历可以输出原数列。笛卡尔树结构由Vuillmin(1980)在解决范围搜索的几何数据结构问题时提出。从数列中构造一棵笛卡尔树可以线性时间完成,需要采用基于的算法来找到在该数列中的所有最近小数。(百度百科)

人话翻译如下:

概念:笛卡尔树是一种二叉树,每个节点有两个值\(key\)\(value\)

性质

  1. 堆的性质。笛卡尔树的树根是这一子树中\(key\)值最小(大)的元素,父节点的键值均小于(或大于)其左右子节点的键值,可以这样递归地弄下去。
  2. 树的中序遍历即为原数组序列。

如图,这样就是一棵原序列的笛卡尔树。(可以结合之前讲的看一下)

构造笛卡尔树

一般,我们规定按\(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;
}
posted @ 2019-09-20 09:47  SilentEAG  阅读(475)  评论(0编辑  收藏  举报