zoukankan      html  css  js  c++  java
  • vijos1194 Domino

    vijos上的题解:

    1.因为每个格子不是被覆盖就是没被覆盖,状态只有0 1两种,m<=5,所以可以将每一列的状态压缩,看作一个二进制数.
    2.矩阵G表示从I状态到J状态的路径条数,自乘N次为长度为N的路径条数。
    3.矩阵乘法可以用快速幂优化,时间复杂度为O(logN*(2^M)^3)可以接受
    4.用I表示前一个状态,J表示这个状态,则I可以到达J的条件是:(K = 2^M - 1 )
    I OR J = K 且 I AND J = AG[W]
    I状态未能填满的格子均由横向1*2的骨牌填充,此时J状态相应的位置也填充了骨牌,因此要满足I OR J = K,即I和J状态此时互补。
    又在J状态下可以继续填充竖向2*1的骨牌,这些骨牌的位置可由I AND J得到,所以I AND J必须满足 I AND J = AG[W]
    AG[W]为在宽度为M是竖向填充2*1的骨牌的所有方案。
    ag:Array[0..7]of longint = (0,3,6,12,15,24,27,30);
    把里面的数字转换成2进制就知道这个数组的干吗用的了。
    5.再次感谢matrix67大牛,膜拜ing...

    还有需要弄清的问题是把每个数字转换为2进制后,1表示此位置被覆盖,0表示未被覆盖。但为什么需要 i  or  j=1<<m -1 并且 i and j=这些奇怪的数字呢?

    其实我们可以这样考虑,我们现在所在的这一行,假设是n行,要过度到n+1行,所以这一行上面的0必须被填上,这样n+1行的同一个位置必定有1,

    这是横着铺了一个骨牌的结果,所以i or j=1<<m-1

    那为什么要有i and j=那些呢?

    我们思考,当把第n行铺满之后,在n+1行上竖着铺放骨牌还可以得到不同的方案数,而这时能铺的只有n行的1所在的位置,而且必须是若干个两个连着的1。

    而 i and j 的1所在位置就是新铺的竖着的骨牌,它必须等于某些特定的值,而不能等于其它的值 比如 01010 这是显然不可能,他是怎么铺上的呢?

    事实上,我们把AG数组的所有转化为二进制即:

    00000,00011,00110,01100,01111,11000,11011,11110

    这为我们提供了所有可行的i and j 的值

    Q.E.D

    代码:

     1 const ag:array[1..8]of longint=(0,3,6,12,15,24,27,30);
     2 type matrix=array[0..35,0..35] of longint;
     3 var a,b:matrix;
     4     i,j,k,m,n,p,ans:longint;
     5 function mo(x:longint):longint;
     6  begin
     7    mo:=x mod p;
     8  end;
     9 procedure init;
    10  begin
    11    readln(n,m,p);
    12    fillchar(a,sizeof(a),0);
    13    fillchar(b,sizeof(b),0);
    14    m:=(1<<m)-1;
    15    for i:=0 to m do
    16     for j:=0 to m do
    17       if i or j=m then
    18        for k:=1 to 8 do
    19         if i and j=ag[k] then a[i,j]:=1;
    20    for i:=0 to m do b[i,i]:=1;
    21  end;
    22 procedure mul(var x,y,z:matrix);
    23  var t:matrix;
    24      i,j,k:longint;
    25  begin
    26    fillchar(t,sizeof(t),0);
    27    for i:=0 to m do
    28     for j:=0 to m do
    29      for k:=0 to m do
    30       t[i,j]:=mo(t[i,j]+x[i,k]*y[k,j]);
    31    z:=t;
    32  end;
    33 procedure ksm(cs:longint);
    34  begin
    35    while cs<>0 do
    36     begin
    37       if cs and 1=1 then mul(a,b,b);
    38       cs:=cs>>1;
    39       mul(a,a,a);
    40     end;
    41  end;
    42 procedure main;
    43  begin
    44    ksm(n);
    45    writeln(b[m,m]);
    46  end;
    47 begin
    48   init;
    49   main;
    50 end.           
    View Code

    还有一个小问题,这里A数组自乘了n次,而且最后直接输出了b[m,m],这是为什么呢?

    我们可以假想初始状态为0行铺满了格子,每乘一次都得到了下一行的结果,所以n此后得到了第n行的结果,A[n,n]表示的就是从第0行铺满到第n行铺满的方案数。

  • 相关阅读:
    Java 反射机制 ( Java Reflection Mechanism )
    Excel&合并单元格内容无效
    UNIX环境高级编程(19-伪终端)
    UNIX环境高级编程(18-终端I/O)
    UNIX环境高级编程(15-进程间通信)
    UNIX环境高级编程(14-高级I/O)
    UNIX环境高级编程(13-守护进程)
    UNIX环境高级编程(12-线程控制)
    UNIX环境高级编程(11-线程)
    C专家编程(4)
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3802506.html
Copyright © 2011-2022 走看看