铺砖问题

题目

Description

用1×2的 砖头铺满N*M的区域,不能有重叠,一共有多少种方案?如下图所示:

铺砖问题_第1张图片

Input

一行输入N和M

Output

输出方案数mod (10^9+7)的值

Sample Input

2 2

Sample Output

2

Data Constraint

20%的数据满足1<=N,M<=6

50%的数据满足1<=N<=100,1<=M<=11

另外50%的数据满足1<=N<=10^200,1<=M<=5

正解

50分

用状压DP。设f[i,j]为第i行的状态为j.0代表就在本行,1代表影响了下一行.

状态

f[i,j]=sum{f[i-1,k]}.答案为f[n,0]

合法性

状态k与j是否合法呢?
1、j and k=0。在两行的统一个位置上不能有两个1,会重叠。
2、j or k的二进制中的所有连续0个数必须为偶数个。因为OR过的0一定是两个二进制都没有影响下一行,这也说明了这OR过的0一定是有横着放的1*2的砖块转移过来的。自然就必须要有偶数个。

注意事项

要先将合法性预处理出来,不然会超时。

100分

用图形变换那题的方法,将他变成矩阵并求出中间矩阵。然后快速幂。

矩阵乘法

见图形变换

快速幂

见图形变换

注意事项

要打高精度除法,要小心。要跟50的方法区分开。这个方法并不适用于前50%的数据。

代码

const
        mo=1000000007;
var
        n,m,i,j,k,ans,total,l,d:longint;
        pq,o,st,ms,ns:string;
        f:array[0..200,0..2048] of longint;
        q,u:array[0..2048] of longint;
        p:array[0..10000] of string;
        h:array[0..2048] of longint;
        middle:array[0..32,0..32] of int64;
        answer:array[0..32] of int64;
        g:array[0..2048,0..2048] of longint;
        bo:boolean;
function zhuan(p:longint):string;
var
        z,i:longint;
        zhuans:string;
begin
        zhuan:='';
        zhuans:='';
        while (p>0) do
          begin
            z:=p mod 2;
            if z=0 then
              zhuans:=zhuans+'0'
            else
              zhuans:=zhuans+'1';
            p:=p div 2;
          end;
        for i:=1 to length(zhuans) do
          begin
            zhuan:=zhuan+zhuans[length(zhuans)-i+1];
          end;
end;
function divide(k:ansistring):ansistring;
var
        i,x:longint;
        p:ansistring;
        a,c:array[1..10000] of longint;
begin
        fillchar(a,sizeof(a),0);
        fillchar(c,sizeof(c),0);
        for i:=1 to length(k) do
          begin
            a[i]:=ord(k[i])-ord('0');
          end;
        x:=0;
        for i:=1 to length(k) do
          begin
            c[i]:=(x*10+a[i]) div 2;
            x:=(x*10+a[i]) mod 2;
          end;
        divide:='';
        for i:=1 to length(k) do
          begin
            str(c[i],p);
            divide:=divide+p;
          end;
        while (divide[1]='0') and (length(divide)>1) do
          delete(divide,1,1);
end;
function zhuan2(k:ansistring):ansistring;
var
        zhuans:ansistring;
        i:longint;
begin
        zhuan2:='';
        zhuans:='';
        while (k>'0') do
          begin
            if (k[length(k)]='1') or (k[length(k)]='3') or (k[length(k)]='5') or (k[length(k)]='7') or (k[length(k)]='9') then
              zhuans:=zhuans+'1'
            else
              zhuans:=zhuans+'0';
            k:=divide(k);
          end;
        for i:=1 to length(zhuans) do
          begin
            zhuan2:=zhuan2+zhuans[length(zhuans)-i+1];
          end;
end;
procedure mi(k1:ansistring);
var
        a,pq:array[0..32,0..32] of int64;
        i,j,k,l:longint;
        pss:ansistring;
begin
        pss:=zhuan2(k1);
        a:=middle;
        for i:=length(pss) downto 1 do
          begin
            if pss[i]='1' then
              begin
                fillchar(pq,sizeof(pq),0);
                j:=1;
                  for k:=0 to d do
                    for l:=0 to d do
                      begin
                        pq[j,k]:=(pq[j,k]+(answer[l]*a[l,k]) mod mo) mod mo;
                      end;
                answer:=pq[j];
              end;
            fillchar(pq,sizeof(pq),0);
            for j:=0 to d do
              for k:=0 to d do
                for l:=0 to d do
                  begin
                    pq[j,k]:=(pq[j,k]+(a[j,l]*a[l,k]) mod mo) mod mo;
                  end;
              a:=pq;
          end;
end;
begin
        readln(st);
        ns:=copy(st,1,pos(' ',st)-1);
        ms:=copy(st,pos(' ',st)+1,length(st));
        val(ms,m);
        val(ns,n);
       if length(ns)<=3  then
          begin
            val(ns,n);
            f[0,0]:=1;
            for i:=0 to 10000 do
              begin
                p[i]:=zhuan(i);
              end;
            d:=1;
            for j:=1 to m do
              d:=d*2;
            dec(d);
            for j:=0 to d do
              begin
                for k:=0 to d do
                  begin
                    pq:=p[(j or k)];
                    if (j and k)<>0 then
                      continue;
                    if (m-length(pq)) mod 2=1 then
                      continue;
                    total:=0;
                    bo:=true;
                    for l:=1 to length(pq) do
                      begin
                        if (pq[l]='0')  then
                          begin
                            total:=total+1;
                          end
                        else
                          begin
                            if total mod 2=1 then
                              begin
                                bo:=false;
                                break;
                              end;
                            total:=0;
                          end;
                      end;
                    if total mod 2=1 then
                      continue;
                    if (bo) then
                      begin
                         inc(h[j]);
                         g[j,h[j]]:=k;
                      end;
                  end;
                end;
                 for i:=1 to n do
                    begin
                     for j:=0 to d do
                       begin
                         if f[i-1,j]=0 then
                           continue;
                       for k:=1 to h[j] do
                         begin
                           f[i,g[j,k]]:=(f[i,g[j,k]]+f[i-1,j]) mod 1000000007;
                         end;
                       end;
                    end;
                 writeln(f[n,0]);
            end
          else
            begin
              for i:=0 to 10000 do
              begin
                p[i]:=zhuan(i);
              end;
            d:=1;
            for j:=1 to m do
              d:=d*2;
            dec(d);
            for j:=0 to d do
              begin
                for k:=0 to d do
                  begin
                    pq:=p[(j or k)];
                    if (j and k)<>0 then
                      continue ;
                    if (m-length(pq)) mod 2=1 then
                      continue;
                    total:=0;
                    bo:=true;
                    for l:=1 to length(pq) do
                      begin
                        if (pq[l]='0')  then
                          begin
                            total:=total+1;
                          end
                        else
                          begin
                            if total mod 2=1 then
                              begin
                                bo:=false;
                                break;
                              end;
                            total:=0;
                          end;
                      end;
                    if total mod 2=1 then
                      continue;
                    if (bo) then
                      begin
                        middle[k,j]:=1;
                      end;
                  end;
                end;
              fillchar(answer,sizeof(answer),0);
              answer[0]:=1;
              mi(ns);
              writeln(answer[0]);
            end;
end.

你可能感兴趣的:(基础算法,DP,预处理,数学)