Largest Rectangle in a Histogram

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 18065    Accepted Submission(s): 5394


Problem 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:
HDU1506(真心不错的DP)_#define
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
 

 

Source
首先吐槽自己手贱非得开ULL导致后面查BUG查了半天mdzz!
纯无思路~~后来扫一眼别人的某片代码(好吧以后不看题解了)
可还是搞了半天,一开始总是想直接一遍DP过去找到最大值,可是发现不行啊,例如(121),(212)这种数据处理起来好麻烦哇!
从大方向出发,找最优解具有的特征,首先最优矩形的高度一定是与给出的某个木板的高度平行的,由贪心理论 可证得,假设存在一个最优解不满足这个条件,显然还可以把他的高度继续往上增加;
所以从这一点出发,得出结论,最优解是由某一个木板,从这个高度出发,无限往左右延伸直至无法延伸而得到的一个矩形!
之所以不能是某个高度无限往左/右某个单一方向延伸得到是因为如果存在凹形数据(212)的话显然这样的方法就失效了,正确答案是3,可是单一方向计算的话只能得出2.
接着就是利用lef和rig数组计算的出,lef[i]:第i块木板往左最长延伸到第几块木板,rig同理不再赘述。
(错误)计算方法显然是:  
if(h[i]<=h[i-1])   lef[i]=lef[i-1];
else lef[i]=i;
但是仔细想想不难发现错误所在,如果这个h[lef[i-1]-1]的高度大于h[i]但是由于h[lef[i-1]-1]<h[lef[i-1]]的话,无疑这个木板及其前面满足条件的木板都会被漏掉!
举个栗子方便理解(1 2 1 1 1) 这样计算的话lef[]={1,2,2,2,2},显然是错的,第五快木板可以直接连到第一块的位置。
那么修改时难道要从得出的位置开始一个一个往前推吗?这样是对的但是显然(MAX_N=10w)的值不会允许我们这样做的,会导致TLE;
仔细想的话会发现,假设k=lef[i](lef[i]已经经过第一次的计算),如果h[k-1]>=h[i]的话那么h[lef[k-1]]也一定大于等于h[i],这样一直迭代加深的找下去肯定会比朴素的查找更快!
代码:
 

#include<bits/stdc++.h>
using namespace std;
#define ULL long long
#define ql(a) memset(a,0,sizeof(a))
ULL max(ULL a,ULL b){return a>b?a:b;}
ULL h[100005],lef[100005],rig[100005];
int n;
ULL solve()
{h[0]=-99999,h[n+1]=-99999;
ULL maxn=0;
ULL i,j,t;
for( i=1;i<=n;++i){
if(i==1) {lef[i]=i;continue;}
if(h[i]<=h[i-1]) lef[i]=lef[i-1];
else lef[i]=i;
int k=lef[i];
while(h[k-1]>=h[i]) k=lef[k-1];
lef[i]=k;

}
for(i=n;i>=1;--i){
if(i==n) {rig[i]=i;continue;}
if(h[i]<=h[i+1]) rig[i]=rig[i+1];
else rig[i]=i;
int k=rig[i];
while(h[k+1]>=h[i]) k=rig[k+1];
rig[i]=k;
}
for(i=1;i<=n;++i){
if(h[i]*(rig[i]-lef[i]+1)>maxn) maxn=h[i]*(rig[i]-lef[i]+1);
}
return maxn;
}
int main()
{
int i,j;
while(cin>>n&&n){
ULL maxn=0;
for(i=1,j=n;i<=n;++i,--j)
scanf("%lld",&h[i]);
printf("%lld\n",solve());
}
return 0;
}