jzoj 高中 3505——积木

Description

小A正在搭积木。有N个位置可以让小A使用,初始高度都为0。小A每次搭积木的时候,都会选定一个拥有相同高度的区间[A..B],然后将位置[A+1..B-1]上的所有积木的高度加一。不幸的是,小A把积木搭好之后没多久,小A调皮的弟弟就将其中若干个位置上的积木弄倒了。小A想知道他原来的积木是如何摆放的,所以他求助于你,请你告诉他原来有多少种可能的摆法。
这里写图片描述
Input
第一行为一个正整数N,表示小A有N个位置。
第二行有N个由空格分隔的整数Hi,表示第i个位置的积木高度。-1表示这个位置上的积木已经被弄倒了。

Output
唯一的一行,输出包括可能的摆法mod 1,000,000,007的结果。

Sample Input
输入1:
3
-1 2 -1

输入2:
3
-1 -1 -1

输入3:
6
-1 -1 -1 2 -1 -1

Sample Output
输出1:
0

输出2:
2

输出3:
3

Data Constraint
对于50%的数据 1<=N<=1000 -1<=Hi<=1000
对于80%的数据 1<=N<=10000
对于100%的数据 1<=N<=20000 -1<=Hi<=10000


dp水法:
设f[i,j]为第i个数,高度为j的方案数,有
f[i,j]=sum(f[i-1,j-1],f[i-1,j],f[i-1,j+1])
如果a[i]没有被破坏,那就除f[i,a[i]]外的状态都不合法。
暴力跑一边,会超一点时。把每次mod,改为统计10次取一次mod就不会超时了。
那么如果直接这样做,空间绝对会爆,因为只记录上一次那么开一个滚动数组就行了


代码:

const mo=1000000007;
var n,i,m,j:longint;
    a,k:array[0..20001]of longint;
    f:array[0..1,-1..20001]of int64;
begin
  assign(input,'brick.in');
  assign(output,'brick.out');
  reset(input);
  rewrite(output);
  readln(n);
  for i:=1 to n do read(a[i]);
  if (a[i]>0) then
    begin
      writeln(0);
      close(input);
      close(output);
      halt; 
    end;
  m:=n div 2+1;
  for i:=1 to n do if i<m then k[i]:=i-1 else k[i]:=n-i;
  f[1,0]:=1;
  for i:=2 to n do
    begin
      fillchar(f[i and 1],sizeof(f[i and 1]),#0);
      if a[i]=-1 then
        begin
          if i mod 10=0 then for j:=0 to k[i] do f[i and 1,j]:=(f[(i-1) and 1,j-1]+f[(i-1) and 1,j]+f[(i-1) and 1,j+1]) mod mo
          else for j:=0 to k[i] do f[i and 1,j]:=f[(i-1) and 1,j-1]+f[(i-1) and 1,j]+f[(i-1) and 1,j+1];
        end
      else f[i and 1,a[i]]:=(f[(i-1) and 1,a[i]-1]+f[(i-1) and 1,a[i]]+f[(i-1) and 1,a[i]+1]) mod mo;
    end;
  writeln(f[n and 1,0] mod mo);
  close(input);
  close(output);
end.
posted @ 2017-07-15 21:54  BEYang_Z  阅读(163)  评论(0编辑  收藏  举报