XJOI 3606 最大子矩形面积/LightOJ 1083 Histogram(单调栈/笛卡尔树)

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 shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3 measured in units where the width of the rectangles is 1.

488c497e53de69fb36cdd05196b6452c?v=15298 

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

Input starts with an integer T (≤ 20), denoting the number of test cases.

Each case contains a line with an integer N (1 ≤ N ≤ 30000) denoting the number of rectangles. The next line contains Nspace separated positive integers (≤ 30000) denoting the heights.

Output

For each case, print the case number and the largest rectangle that can be made.

Sample Input

2

7

2 1 4 5 1 3 3

5

4 4 3 2 4

Sample Output

Case 1: 8

Case 2: 10

题意:有一系列的长条状的矩形,宽度都为1,相邻的竖立在x轴上,求最大的子矩形

 

胡扯:

有一回对我说道,“你学过算法么?”我略略点一点头。他说,“学过算法,……我便考你一考。XJOI的最大子矩形面积,怎样写的?”我想,AKIOI的人,怎么也来考我?便回过脸去,不再理会。孔乙己等了许久,很恳切的说道,“不能写罢?……我教给你,记着!这些算法应该记着。将来AKIOI的时候,秒题要用。”我暗想我和IOI的等级还很远呢,而且IOI也从不将这种题搬去;又好笑,又不耐烦,懒懒的答他道,“谁要你教,不是用单调栈胡搞毛搞么?”孔乙己显出极高兴的样子,将两个指头的长指甲敲着键盘,点头说,“对呀对呀!……最大子矩形面积有四样写法,你知道么?”我愈不耐烦了,努着嘴走远。孔乙己刚打开了c++,想在上面写代码,见我毫不热心,便又叹一口气,显出极惋惜的样子。

 

题解:

这题的确有四种解法

单调栈、笛卡尔树、类似kmp的解法、一种st表

嗯,后面两种我连名字都说不准确,所以还是不讲了

主要写两种常用的解法

首先是单调栈

单调栈就是一种栈内单调的数据结构,至于怎么维护,比如说一个单调递增的单调栈,你只需要在每加入一个数的时候,把所有比当前数大的栈顶全部弹掉就可以了

如果一个数被弹掉了,说明他能控制的矩形也已经到头了,所以就可以统计答案了,答案就是高度乘以(出去时间-进入时间)

然后唯一要注意的是该数的进入时间是第一个比他小的数的进入时间+1(他在栈中时下面的元素),而不是i,如果直接减的话就用进入时间就可以了

代码如下:

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct node
{
    int time,val;
};
int n,a[100010],b[100010];
stack<node> s;
long long ans;

int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1; i<=n; i++)
    {
        int w=i;
        while((!s.empty())&&s.top().val>a[i])
        {
            int ti=s.top().time;
            w=ti;
            int v=s.top().val;
            ans=max(ans,1ll*(i-ti)*v);
            s.pop();
        }
        s.push((node){w,a[i]});
    }
    while(!s.empty())
    {
        int ti=s.top().time;
        int v=s.top().val;
        ans=max(ans,1ll*(n-ti+1)*v);
        s.pop();
    }
    printf("%lld\n",ans);
}

然后是笛卡尔树

笛卡尔树的中序遍历就是原序列(二叉搜索树性质),而他的每个节点的值都保证是其子树中的极值(堆性质)

笛卡尔树是可以O(n)构建的

将点i先扔到i-1的儿子下,然后比较i-1的所有祖先,一直找到第一个值比a[i]小的祖先,把这颗祖先的右儿子改成他,把原来的右儿子改成他的左儿子

均摊分析复杂度是O(n)

建出了树可以直接用size*tr[i].num来算出最大子矩形面积

不过我是直接记录每个点的pos然后瞎搞的

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

struct node
{
    int fa,ls,rs,num,pos;
}tr[100010];
int a[100010],n;
long long ans;

void dfs(int pos,int l,int r)
{
    if(l>r) return ;
    ans=max(ans,1ll*(r-l+1)*a[pos]);
    dfs(tr[pos].ls,l,tr[pos].pos-1);
    dfs(tr[pos].rs,tr[pos].pos+1,r);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }    
    tr[0].num=0;
    tr[0].rs=1;
    tr[1].num=a[1];
    tr[1].pos=1;
    for(int i=2;i<=n;i++)
    {
        tr[i].num=a[i];
        tr[i].pos=i;
        int p;
        for(p=i-1;tr[i].num<tr[p].num;p=tr[p].fa);
        tr[i].ls=tr[p].rs;
        tr[p].rs=i;
        tr[i].fa=p;
    }
    dfs(tr[0].rs,1,n);
    printf("%lld\n",ans);
}

 

 

 

 

 

posted @ 2018-07-19 10:32  Styx-ferryman  阅读(481)  评论(0编辑  收藏  举报