hihoCoder 1143 : 骨牌覆盖问题·一(递推,矩阵快速幂)

【题目链接】:click here~~

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

骨牌,一种古老的玩具。今天我们要研究的是骨牌的覆盖问题:

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

举个例子,对于长度为1到3的棋盘,我们有下面几种覆盖方式:

提示:骨牌覆盖

提示:如何快速计算结果

输入

第1行:1个整数N。表示棋盘长度。1≤N≤100,000,000

输出

第1行:1个整数,表示覆盖方案数 MOD 19999997

样例输入
62247088
样例输出
17748018

【思路】矩阵快速幂

我们考虑在已经放置了部分骨牌(灰色)的情况下,下一步可以如何放置新的骨牌(蓝色):

最右边的一种情况是不可能发生的,否则会始终多一个格子没有办法放置骨牌。或者说灰色部分的格子数为奇数,不可能通过1x2个骨牌放置出来。

那么通过对上面的观察,我们可以发现:

在任何一个放置方案最后,一定满足前面两种情况。而灰色的部分又正好对应了长度为N-1和N-2时的放置方案。由此,我们可以得到递推公式:

f[n] = f[n-1] + f[n-2];

这个公式是不是看上去很眼熟?没错,这正是我们的费波拉契数列。

f[0]=1,f[1]=1,f[2]=2,...

当N很小的时候,我们直接通过递推公式便可以计算。当N很大的时候,只要我们的电脑足够好,我们仍然可以直接通过递推公式来计算。

但是我们学算法的,总是这样直接枚举不是显得很Low么,所以我们要用一个好的算法来加速(装X)。

事实上,对于这种线性递推式,我们可以用矩阵乘法来求第n项。对于本题Fibonacci数列,我们希望找到一个2x2的矩阵M,使得(a, b) x M = (b, a+b),其中(a,
b)和(b, a+b)都是1x2的矩阵。

显然,只需要取M = [0, 1; 1, 1]就可以了:

进一步得到:

那么接下来的问题是,能不能快速的计算出M^n?我们先来分析一下幂运算。由于乘法是满足结合律的,所以我们有:

不妨将k[1]..k[j]划分的更好一点?

其中(k[1],k[2]...k[j])2表示将n表示成二进制数后每一位的数字。上面这个公式同时满足这样一个性质:

结合这两者我们可以得到一个算法:

1. 先计算出所有的{a^1, a^2, a^4 ... a^(2^j)},因为该数列满足递推公式,时间复杂度为O(logN)

2. 将指数n二进制化,再利用公式将对应的a^j相乘计算出a^n,时间复杂度仍然为O(logN)

则总的时间复杂度为O(logN)

这种算法因为能够在很短时间内求出幂,我们称之为“快速幂”算法。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL MOD=19999997;
LL N;
int i,j;
struct Matrlc
{
    LL mapp[2][2];
} ans,base;
Matrlc unit= {1,0,0,1};
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] =base.mapp[0][1]=base.mapp[1][0]=1;
    base.mapp[1][1]=0;
    ans.mapp[0][0] = ans.mapp[1][1] = 1;// ans 初始化为单位矩阵
    ans.mapp[0][1] = ans.mapp[1][0] = 0;
    while(n)
    {
        if(n&1)   ans=mult(ans,base);
        base=mult(base,base);
        n>>=1;
    }
    return ans.mapp[0][1]%MOD;
}
int main()
{
   scanf("%lld",&N);
   printf("%lld\n",pow(N+1)%MOD);
   return 0;
}
/*
题目:
首先,这道题目是一道斐波那契数列的题目。
我们来分析一下,第三个图形是如何由前两个图形组成。
 ______           _______
|    | |   或    |  |____|
|____|_|         |__|____|

扩展到第n个图形,我们有:
 _____________           ______________
|           | |  或     |         |____|
|___________|_|         |_________|____|
所以,f(n)=f(n-1)+f(n-2)

由于n可能会很大,所以我们需要一些计算的技巧。
斐波那契数列是可以由矩阵计算得到,如下:

[a,b]* [0,1] = [b,a+b]
       [1,1]

令mat =[0,1]
       [1,1]

那么,理论上,我们乘以n个矩阵mat,就可以求得f(n),
但是n个矩阵相乘,时间复杂度为O(n),
这时候,我们采用快速幂运算来求解,可以把时间复杂度降为O(logn)。

*/

#include<string>
#include <iomanip>
#include<fstream>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
//#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
//#include<stack>
#include<vector>
#include <algorithm>
using namespace std;
#define MOD 19999997
class matrix22
{
	public:
	long long a1, a2;
	long long  b1, b2;
	matrix22() :a1(0), a2(1), b1(1), b2(1){};
	matrix22 operator*(const matrix22 tmp)     // 重载矩阵乘法
	{
		matrix22 mat;
		mat.a1 = (a1%MOD)*(tmp.a1%MOD) + (a2%MOD)*(tmp.b1%MOD);
		mat.a2 = (a1%MOD)*(tmp.a2%MOD) + (a2%MOD)*(tmp.b2%MOD);
		mat.b1 = (b1%MOD)*(tmp.a1%MOD) + (b2%MOD)*(tmp.b1%MOD);
		mat.b2 = (b1%MOD)*(tmp.a2%MOD) + (b2%MOD)*(tmp.b2%MOD);
		return mat;
	}
};
/*
函数名  :main
函数功能:主函数
*/
int main(void)
{
	int n;
	scanf("%d", &n);
	int dp1 = 1;
	int dp2 = 2;
	if (n <= 0) printf("0\n");
	else if (n == 1) printf("1\n");
	else if (n == 2) printf("2\n");
	else
	{
		n -= 3;
		matrix22 mat;
		matrix22 ans;
		while (n != 0)
		{
			//如果二进制该位为1,则ans*mat
			if (n & 1)
				ans = ans*mat;
			//mat每次与自身相乘,求得矩阵的1,2,4,8,16次方
			mat = mat*mat;
			n = (n >> 1);
		}
		//输出f(n)
		long long answer =( ans.a2 + 2 * ans.b2)%MOD;
		cout << answer << endl;

	}
	return  0;
}

时间: 2024-10-09 03:55:16

hihoCoder 1143 : 骨牌覆盖问题·一(递推,矩阵快速幂)的相关文章

2014 BNU 邀请赛E题(递推+矩阵快速幂)

Elegant String 题意:给定一个字符串,由0-k数字组成,要求该串中,子串不包含0-k全排列的方案数 思路:dp[i][j]表示放到i个数字,后面有j个不相同,然后想递推式,大概就是对应每种情况k分别能由那几种状态转移过来,在纸上画画就能构造出矩阵了,由于n很大,所以用快速幂解决 代码: #include <stdio.h> #include <string.h> const long long MOD = 20140518; int t; long long n; i

HDU 5950 Recursive sequence 【递推+矩阵快速幂】 (2016ACM/ICPC亚洲区沈阳站)

Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 249    Accepted Submission(s): 140 Problem Description Farmer John likes to play mathematics games with his N cows. Recently, t

hdu 2604 递推 矩阵快速幂

HDU 2604 Queuing (递推+矩阵快速幂) 这位作者讲的不错,可以看看他的 #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <cstring> using namespace std; const int N = 5; int msize, Mod; struct Mat { int mat[N][N]; }; M

HDU 2604 Queuing (递推+矩阵快速幂)

[题目链接]:click here~~ [题目大意]: n个人排队,f表示女,m表示男,包含子串'fmf'和'fff'的序列为O队列,否则为E队列,有多少个序列为E队列. [思路]: 用f(n)表示n个人满足条件的结果,那么如果最后一个人是m的话,那么前n-1个满足条件即可,就是f(n-1): 如果最后一个是f那么这个还无法推出结果,那么往前再考虑一位:那么后三位可能是:mmf, fmf, mff, fff,其中fff和fmf不满足题意所以我们不考虑,但是如果是 mmf的话那么前n-3可以找满足

HDU2604-Queuing(递推+矩阵快速幂)

题目链接 题意:男为f,女为m,求在长度为L的队列中不存在fmf,fff这样子序列的序列的个数. 思路:又是递推题,假设长度为L的队列中存在的序列个数为f(L),那么考虑最后一个放的字母,假设最后一个放m,那么前L-1个可以随意排列,即个数为f(L - 1):如果最后一个放f,那么考虑后两个字母,可能出现的情况为ff,mf,这样比较难判断是否符合题目要求的,所以我们考虑后三个字母,可能出现的情况就为fff,mff,fmf,mmf,显而易见mff,mmf符合题意.当后三个为mmf时,前L-3个可以

HDU 2842 (递推+矩阵快速幂)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2842 题目大意:棒子上套环.第i个环能拿下的条件是:第i-1个环在棒子上,前i-2个环不在棒子上.每个环可以取下或放上,cost=1.求最小cost.MOD 200907. 解题思路: 递推公式 题目意思非常无聊,感觉是YY的. 设$dp[i]$为取第i个环时的总cost. $dp[1]=1$,$dp[2]=2$,前两个环取下是没有条件要求的. 从i=3开始,由于条件对最后的环限制最大,所以从最后一

2017中国大学生程序设计竞赛 - 女生专场 Happy Necklace(递推+矩阵快速幂)

Happy Necklace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1146    Accepted Submission(s): 491 Problem Description Little Q wants to buy a necklace for his girlfriend. Necklaces are single

HDU6185 Covering (递推+矩阵快速幂)

大致题意:让你用1*2规格的地毯去铺4*n规格的地面,告诉你n,问有多少种不同的方案使得地面恰好被铺满且地毯不重叠.答案对1000000007取模 递推得f(n)=f(n-1)+5*f(n-2)+f(n-3)-f(n-4),因为n很大,所以接下来用矩阵快速幂搞搞就可以了. #include <iostream> #include <cstring> #include <cstdio> using namespace std; long long n; long long

HDU - 6185 Covering(暴搜+递推+矩阵快速幂)

Covering Bob's school has a big playground, boys and girls always play games here after school. To protect boys and girls from getting hurt when playing happily on the playground, rich boy Bob decided to cover the playground using his carpets. Meanwh