ZCMU-1618-骨牌覆盖(矩阵乘法+快速幂)

1618: 骨牌覆盖1

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 264   Solved: 124
[ Submit][ Status][ Web Board]

Description

我们有一个2xN的长条形棋盘,然后用1x2的骨牌去覆盖整个棋盘。对于这个棋盘,一共有多少种不同的覆盖方法呢?

Input

输入n,n<=100000

Output

覆盖方案总数对19999997取余

Sample Input

1
2

Sample Output

1
2

【解析】
这道题的话其实我们只要列举出前面几个数我们数一数方案我们就能知道是什么样的规律了,感觉很多题都是斐波那契数列,就算蒙,说不定也能蒙对了,还有这道题数据不大,所以我们更加好做,不过我在这里要说一下万一数据很大,我们怎么求斐波那契数列?在这里我要说一下矩阵乘法了。什么是矩阵乘法?把这些概念搞懂就差不多了。 1.当矩阵A的列数等于矩阵B的行数时,A与B可以相乘。2.矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。3.乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。比如
我们可以用矩阵乘法来求n项,我们希望找到一个2x2的矩阵M,使得(a, b) x M = (b, a+b),其中(a, b)和(b, a+b)都是1x2的矩阵。a代表前一项,b代表的是后一项。我们这里M只要取[0,1;1,1];至于为什么我们可以自己去验证下。我们可以看一下图片上的例子。
这就是我们要做的事情矩阵直接不断的成不断的乘,最终我们的式子是这样子的
这个时候n其实是巨大的,我们这个时候想要快速的求出我们就需要用到快速幂的算法了。关于快速幂的问题可以看看这里http://blog.csdn.net/zcmuczx/article/details/53962502
我们很容易的知道a的n次方我们要求,就把n转换成二进制来求,因为你看5如果给他编码我们就取后几位101遇到1就表示它要开始乘了,就好比a的5次方其实就是a的4次方乘a所以我们遇到末尾的1相当于遇到5的1次乘起来,遇到倒数第三位的1就是5的四次了大家可以自己试一下。大概就是这么的思路接下来具体的看代码。
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
#define LL long long
#define MOD 19999997
LL N;
int i,j;
struct Matrlc
{
    LL mapp[2][2];//定义结构体,元素是一个矩阵
} ans,base;
Matrlc mult(Matrlc a,Matrlc b)
{
    Matrlc c;
    for(int i=0; i<2; i++)
    {
        for(int j=0; j<2; j++)
        {
            c.mapp[i][j]=0;
            for(int k=0; k<2; k++)//矩阵乘法
            {
                c.mapp[i][j]+=(a.mapp[i][k]*b.mapp[k][j])%MOD;//这里是比如行的元素乘列的元素
            }
            c.mapp[i][j]%=MOD;
        }
    }
    return c;
}
LL pow(LL n)
{
    base.mapp[0][0] =0;
    base.mapp[0][1]=base.mapp[1][0]=base.mapp[1][1]=1;//初始化矩阵成0,1,1,1;
    ans.mapp[0][0] =1; ans.mapp[0][1] =0;// 把第一个矩阵我把他设为1,0,0,0,这样的话其实只有第一行的元素是有用的经过一次乘法
    ans.mapp[1][0]= ans.mapp[1][1] = 0;//变成了1,1,0,0这样就表示f[1]为1其实这样才是初始状态,所以我们下面才要pow(N+1)
    while(n)
    {
        if(n&1)
            ans=mult(ans,base);
        base=mult(base,base);//矩阵乘法
        n>>=1;
    }
    return ans.mapp[0][1]%MOD;
}
int main()
{
   while(~scanf("%lld",&N))
   {
   printf("%lld\n",pow(N+1)%MOD);
   }
   return 0;
}


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值