【题目描述】
LazyChild在青岛二中科技楼里种了一排n棵树,每棵树都有一个高度。他会枚举所有的区间,然后从区间中找出一个高度最矮的树进行浇水(照顾弱者)。由于LazyChild浇完水之后就精疲力竭了,所以请你帮助他计算每棵树都被浇了几次水。
【输入文件】
第一行一个整数n。
第二行n个整数,分别表示每棵树的高度。
【输出文件】
一行n个整数用空格隔开,分别表示每棵树被浇了几次水。
【样例输入】
3
1 3 5
【样例输出】
3 2 1
【样例解释】
LazyChild枚举到了6个区间分别是[1],[3], [5], [1 3], [3 5], [1 3 5],对应的最矮的树的高度是1, 3, 5, 1, 3, 1。
【数据规模和约定】
对于40%的数据,n <= 1000
对于100%的数据,n <= 1000000,保证每棵树的高度都不相同
【考察点】
乘法原理的运用
【思路】
首先想到的肯定是暴力枚举,但是这样做只能通过40%的数据。于是我们就来考虑优化问题:首先来看,某棵树要被浇水的条件是它在某区间内是最小的,于是我们就很容易想到单调队列,但是这个区间长度不一定,所以单调队列不能解决问题;那么再看,我们可以发现,在某个区间内,如果某个点是最小的,那么只要是包含这个点的子区间,都是对他浇水,于是我们就可以考虑枚举这个点,并且分别向左、向右扫描,找出第一个小于他的位置,根据乘法原理,可以得出,这个点的浇水次数为L*R,但是这样的最坏情况是N^2的(每个点都扫描整个数列,虽然题目保证高度不同,达不到这个程度,但是毕竟不保险)。所以我们就来思考如何在更低的时间复杂度内求出每个点的L、R:观察可以发现,某点的L、R可以由附近的点推出(结论就不说了,自己推导看看),于是就能AC了。
【提交情况】
第1次WA6组,第2次AC
【经验】
L*R可能会爆Longint,另外这样的语句S:=L*R,即使L、R分别没爆Longint,S是int64的,若L*R爆掉了Longint,S中存的就是爆掉之后的值。因为它的计算原理是先求出L*R的32位长整,再将这个数强制转换成64位长整……
【收获】
经验中的那个东西……
Programwater;
Var
n:longint;
l,r,a:array[0..1000001]of longint;
sum:array[0..1000001]of int64;
Procedureterminate;
begin
close(input);
close(output);
halt;
end;
Procedureinit;
var
i:longint;
begin
assign(input,'water.in');
assign(output,'water.out');
reset(input);
rewrite(output);
readln(n);
for i:=1 to n do
begin
read(a[i]);
end;
end;
Functioncon1(x,t:longint):int64;
begin
if a[l[x]]<a[t] then exit(l[x])
else
begin
exit(con1(l[x],t));
end;
end;
Functioncon2(x,t:longint):int64;
begin
if a[r[x]]<a[t] then exit(r[x])
else
begin
exit(con2(r[x],t));
end;
end;
Proceduremain;
var
i:longint;
t1,t2:int64;
begin
a[0]:=-999999999;
a[n+1]:=-999999999; //边界给个极小值
for i:=1 to n do
begin
if a[i-1]>a[i] thenl[i]:=con1(i-1,i)
else l[i]:=i-1;
end; //推l
for i:=n downto 1 do
begin
if a[i]>a[i+1] then r[i]:=i+1
else r[i]:=con2(i+1,i);
end; //推r
for i:=1 to n do
begin
t1:=i-l[i];
t2:=r[i]-i; //计算左边、右边的数
sum[i]:=t1*t2;
//万恶的计算……
end;
for i:=1 to n do
write(sum[i],' ');
end;
Begin
init;
main;
terminate;
End.
ACCode: