【数学题】浇水解题报告

来源:互联网 发布:fmea软件 编辑:程序博客网 时间:2024/05/01 05:47
浇水(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


原创粉丝点击