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

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/75192033

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.
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页