jzoj. 3505. 【NOIP2013模拟11.4A组】积木(brick)

来源:互联网 发布:炒股模拟软件app 编辑:程序博客网 时间:2024/06/05 09:23

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:

-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

分析:
(水解):
设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就不会超时了。
(正解):对于每两个有确定高度的点,即a[i]≠-1(或者是两端的0),考虑中间的方案数。
设a[i]和a[j]≠-1,则
对于集合
{a[i],a[i+1],a[i+2],……a[j]}
因为相邻两个数的差不超过1,则得到集合
{a[i+1]-a[i],a[i+2]-a[i],……a[j]-a[j-1]}
这个集合的每个元素的值只可能是-1,0,1,且这些元素的和为sum=a[j]-a[i]。
如果sum>0,删去其中sum个1,剩余元素和为0;
如果sum<0,删去其中 |sum| 个-1,剩余原始和为0;
其实这两种状态时等价的。
这时我们得到了一个和为0的集合。
设i到j中间有m个元素,其中1有x个,则-1有x个,0就是有(m-2x-sum)个(其实这个不重要)。
那方案数为C(m,sum+x)*C(m-sum-x,x)
{中间有m个空位,1有sum+x个;对于剩余的m-sum-x个位置,-1有x个;剩余的位置都是0,方案显然是1,相乘结果不变}
我们应该先预处理出n! (当然要取mod啦),然后每次运算都有求组合数,以为要除以某些已经mod了的数,要预处理出n!的逆元。因为mod p(p为质数),可以用费马小定理或欧拉定理得到。
(n!)^(-1)=(n!)^(p-2)

代码(水解):

const modd=1000000007;var f:array [0..1,-1..20001] of int64; a,max:array [0..20001] of longint; n,i,j,x,s,t,m,y:longint;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[1]>0 then  begin   writeln(0);   close(input);   close(output);   exit;  end; m:=n div 2+1; for i:=1 to n do  if i<m then max[i]:=i-1 else max[i]:=n-i; x:=0; f[x,0]:=1; for i:=2 to n do  begin   y:=x;   x:=1-x;   fillchar(f[x],sizeof(f[x]),0);   if a[i]=-1 then    begin     if i mod 10=0 then         for j:=0 to max[i] do          f[x,j]:=(f[y,j-1]+f[y,j]+f[y,j+1])mod modd        else         for j:=0 to max[i] do          f[x,j]:=f[y,j-1]+f[y,j]+f[y,j+1];    end   else     f[x,a[i]]:=(f[y,a[i]-1]+f[y,a[i]]+f[y,a[i]+1])mod modd;  end; writeln(f[x,0] mod modd); close(input); close(output);end.

代码(正解)(by:jzoj某大神,本人仅转载):

const mo=1000000007;var    n,i,j,k,l,h:longint;    a:array[1..20000]of longint;    jc,ny:array[0..20000]of longint;    ans,tot:longint;function ksm(x,y:longint):longint;begin    if y=1 then exit(x);    ksm:=ksm(x,y>>1);ksm:=int64(ksm)*ksm mod mo;    if y and 1=1 then ksm:=int64(ksm)*x mod mo;end;function c(a,b:longint):longint;begin    if a>b then exit(0);    exit(int64(jc[b])*ny[a]mod mo*ny[b-a]mod mo);end;begin    assign(input,'brick.in');reset(input);    assign(output,'brick.out');rewrite(output);    readln(n);for i:=1 to n do read(a[i]);    jc[0]:=1;for i:=1 to n do jc[i]:=int64(jc[i-1])*i mod mo;    ny[n]:=ksm(jc[n],mo-2);for i:=n-1 downto 0 do ny[i]:=int64(ny[i+1])*(i+1)mod mo;    if(a[1]>0)or(a[n]>0)then    begin        writeln(0);halt;    end;    a[1]:=0;a[n]:=0;i:=1;j:=1;ans:=1;    while i<n do    begin        inc(i);if a[i]>-1 then        begin            tot:=0;h:=abs(a[i]-a[j]);            for k:=0 to i-j-h do            begin                l:=i-j-k;if (l+h)and 1=1 then continue;                tot:=(tot+int64(c(k,i-j))*(c((l+h)>>1,l)-c((l+a[i]+a[j]+2)>>1,l))mod mo+mo)mod mo;            end;            ans:=int64(tot)*ans mod mo;j:=i;        end;    end;    writeln(ans);end.
阅读全文
0 0
原创粉丝点击