【数学题】浇水解题报告

浇水 (water.c/cpp/pas)

【题目描述】

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的(每个点都扫描整个数列,虽然题目保证高度不同,达不到这个程度,但是毕竟不保险)。所以我们就来思考如何在更低的时间复杂度内求出每个点的LR:观察可以发现,某点的LR可以由附近的点推出(结论就不说了,自己推导看看),于是就能AC了。

【提交情况】

1WA6组,第2AC

【经验】

L*R可能会爆Longint,另外这样的语句S:=L*R,即使LR分别没爆LongintSint64的,若L*R爆掉了LongintS中存的就是爆掉之后的值。因为它的计算原理是先求出L*R32位长整,再将这个数强制转换成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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值