hdu 1506 Largest Rectangle in a Histogram

Written with StackEdit.

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:
图片来源Wikipedia
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 (h_1, ..., h_n), where (0 <= h_i <= 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

Solution

  • 考虑构造一颗笛卡尔树.
  • 笛卡尔树类似于(treap),(key)值满足二叉搜索树的性质,(value)满足堆的性质(此题构造的小根堆).
  • 将位置作为(key),高度作为(value),建好树后(如下图),答案显然为每个点的(value)乘上子树大小,取一个最大值.(结合题意及笛卡尔树的性质感性理解).
    图片来自Wikipedia
  • 那么现在的主要问题是如何构造这样一颗笛卡尔树.
  • 如果直接写一颗(treap),暴力插入,为(O(nlogn)).
  • 但这里只用构造树,我们有更优秀的(O(n))做法.

注意:只有当(key)值单调递增时才能(O(n))做,原因下面会看到.

  • 对这棵树的极右链维护一个栈,即从根节点出发,一直向右走直到底部的路径.栈底为根,栈顶为叶子节点.那么从栈顶到栈底,(val)值是不上升的.
  • 我们对于一个新节点(i),找栈中第一个(val)值小于(i)的,记为(j),那么(i)一定是(j)的右儿子((key)大,(value)小).
  • (j)原来的右儿子会成为(i)的左儿子((key)小,(value)小).
  • 此时极右链中,(j)原来的右儿子已经为(i)的左儿子,下方的链全部断掉,都移出栈,(i)进栈.(可以画图帮助理解)
  • 我们能确定这三个点新的连接方式,是用到了(key)值单调递增.所以这是一个前提条件.否则需要先排序,也就退化成了(O(nlogn)).
  • 每个点最多进栈一次,出栈一次,复杂度(O(n)).
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		{
			fh=-1;
			jp=getchar();
		}
	while (jp>='0'&&jp<='9')
		{
			out=out*10+jp-'0';
			jp=getchar();
		}
	return out*fh;
}
const int MAXN=1e5+10;
struct CartesianTree{
	struct node{
		int key,value;
		int lson,rson;
		int siz;
	}tree[MAXN];
	int top,stk[MAXN];
	int root,minv;
	int n;
	void init()	
		{
			top=0;
			minv=0x7fffffff;
		}
	void Build(int *key,int *val,int siz)//小根堆+二叉搜索树 
		{
			n=siz;
			for(int i=1;i<=n;++i)
				{
					tree[i].lson=tree[i].rson=0;		
					while(top && val[i]<val[stk[top]])
						{
							tree[i].lson=stk[top];
							--top;
						}
					if(top)
						tree[stk[top]].rson=i;
					stk[++top]=i;
					tree[i].key=key[i];
					tree[i].value=val[i];
					if(val[i]<minv)
						minv=val[i],root=i;
				}
		}
	void dfs(int u)
		{
			tree[u].siz=1;
			if(tree[u].lson)
				dfs(tree[u].lson),tree[u].siz+=tree[tree[u].lson].siz;;
			if(tree[u].rson)
				dfs(tree[u].rson),tree[u].siz+=tree[tree[u].rson].siz;
		}
	LoveLive solve()
		{
			dfs(root);
			LoveLive res=-1;
			for(int i=1;i<=n;++i)
				{		
					res=max(res,1LL*tree[i].value*tree[i].siz);		
				}
			return res;
		}
}T;
int n;
int h[MAXN],id[MAXN];
int main()
{
	while(scanf("%d",&n) && n)
		{
			T.init();
			for(int i=1;i<=n;++i)
				h[i]=read(),id[i]=i;
			T.Build(id,h,n);
			LoveLive ans=T.solve();
			printf("%lld
",ans);
		}
	return 0;
}
原文地址:https://www.cnblogs.com/jklover/p/10126043.html