poj2559 & zoj1985 &hdu1506 Largest Rectangle in a Histogram(笛卡尔树)

Largest Rectangle in a Histogram
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 12205   Accepted: 3957

Description

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles: 
poj2559 & zoj1985 &hdu1506 Largest Rectangle in a Histogram(笛卡尔树)_第1张图片
Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.

Input

The input contains several test cases. Each test case describes a histogram and starts with an integer  n, denoting the number of rectangles it is composed of. You may assume that  1<=n<=100000. Then follow  n integers h1,...,hn, where  0<=hi<=1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is  1. A zero follows the input for the last test case.

Output

For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.

Sample Input

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

Sample Output

8
4000

Hint

Huge input, scanf is recommended.

Source

Ulm Local 2003

题目大意:给定一个统计直方图,n个矩形条,每个矩形条宽1,高0-10^9,求最大矩形面积。
题目分析:这题数据好像比较水,当年各种乱搞也能过,今天看了一下笛卡尔树,发现这题用笛卡尔树就能过了。与前面2题不同的是,这题没那么裸,要抽象出笛卡尔树的模型。
做法是这样的,以输入的顺序为第一关键字,矩形条的高为第二关键字建立一颗笛卡尔树。并且以高为关键字是一个小堆。那么从树根开始后序遍历整颗树,在节点处结算一次保存最大值。建树O(n),遍历树也是O(n),所以总的时间复杂度也是O(n),而且常数非常小,所以比较高效,在3个oj上提交跑的都还可以,不在第一版就在第二版。
下面简单分析一下笛卡尔树解这题的原理。首先笛卡尔树是一个二叉排序树,那么以输入顺序作为二叉排序树的关键字,所建出来的树保证以任意节点为根的子树的所有节点是连续的(想一想,为什么),然后笛卡尔树以矩形条的高度为关键字又有堆的性质,而这题的关键不就正是要找连续的矩形条,使总面积最大,而决定大矩形高度的就是每个小矩形条的最小高度!那么我们维护一个小堆即可,这样在每个节点处就保证以该节点为根的子树所能构成的最大矩形就是该节点的高度*该子树的大小!!
因此本题非常巧妙的运用了笛卡尔树的2个性质,此题就是为笛卡尔树而生的啊有木有!!
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 100005;
typedef __int64 ll;
ll ans;
struct node
{
    ll val;
    int l,r,pa;
}lcm[N];
int n;
int stack[N];

int nextint()
{
    char c;
    int ret;
    while(isspace(c = getchar()))
        ;
    ret = c - '0';
    while((c = getchar()) >= '0' && c <= '9')
        ret = ret * 10 + c - '0';
    return ret;
}

int build()
{
    int i,j,top;
    top = -1;
    for(i = 1;i <= n;i ++)
    {
        j = top;
        while(j >= 0 && lcm[stack[j]].val > lcm[i].val)
            j --;
        if(j != -1)
        {
            lcm[i].pa = stack[j];
            lcm[stack[j]].r = i;
        }
        if(j < top)
        {
            lcm[stack[j + 1]].pa = i;
            lcm[i].l = stack[j + 1];
        }
        stack[++ j] = i;
        top = j;
    }
    return stack[0];
}

int dfs(int cur)
{
    if(cur == -1)
        return 0;
    int num = dfs(lcm[cur].l) + dfs(lcm[cur].r) + 1;
    //printf("%I64d:%d\n",lcm[cur].val,num);
    ll tmp = num * lcm[cur].val;
    if(tmp > ans)
        ans = tmp;
    return num;
}

void print(int cur)
{
    if(cur == -1)
        return;
    printf("%d\n",lcm[cur].val);
    print(lcm[cur].l);
    print(lcm[cur].r);
}

int main()
{
    int i;
    while(n = nextint(),n)
    {
        for(i = 1;i <= n;i ++)
        {
            lcm[i].val = (ll)nextint();
            lcm[i].l = lcm[i].r = lcm[i].pa = -1;
        }
        int root = build();
        //print(root);
        ans = 0;
        dfs(root);
        printf("%I64d\n",ans);
    }
    return 0;
}
//poj:6216K	47MS
//zoj:70ms	2532k
//hdu:31MS	3548K


你可能感兴趣的:(笛卡尔树)