题目链接
题意:4*n的矩阵中用1*2或者2*1的多米诺骨牌骨牌填充,问有多少种方案,这个n很大1e18
思路:n很大肯定不能用插头dp,所以就要用正常的dp来推,再加上矩阵快速幂。这题的dp式子比较难弄,因为dp[n]和之前的所有项都有关系(包括dp[0]),具体关系就是距离n为偶数的项乘三,距离n为基数的项乘二,然后dp[n-1]和dp[n-2]要特殊处理一下。为了方便统计,所以我在矩阵里维护sum1[i]和sum2[i]前者表示距离i奇数项的和,后者表示距离i偶数项的和。
具体的矩阵如下,代码就是改下模版就好很简单
⎡⎣⎢⎢⎢⎢dp[n]dp[n−1]sum1[n]sum2[n]⎤⎦⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢1110100030012010⎤⎦⎥⎥⎥⎥∗⎡⎣⎢⎢⎢⎢dp[n−1]dp[n−2]sum1[n−1]sum2[n−2]⎤⎦⎥⎥⎥⎥
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <stack>
using namespace std;
#define push_back pb
#define pi acos(-1)
const long long mod = 1000000007;
#define maxn 111111
#define maxm 11111
using namespace std;
struct mat {
long long a[7][7];
};
mat mul(mat x, mat y) {
mat tt;
for(int i = 1; i <= 4; i++) {
for(int j = 1; j <= 4; j++) {
tt.a[i][j] = 0;
for(int k = 1; k <= 4; k++) {
tt.a[i][j] += x.a[i][k] * y.a[k][j] % mod;
tt.a[i][j] %= mod;
}
}
}
return tt;
}
int main()
{
long long n;
while(scanf("%lld", &n) != EOF) {
if(n == 1) {
printf("1\n");
continue;
}
if(n == 2) {
printf("5\n");
continue;
}
mat temp, ans;
temp.a[1][1] = 1; temp.a[1][2] = 1; temp.a[1][3] = 3; temp.a[1][4] = 2;
temp.a[2][1] = 1; temp.a[2][2] = 0; temp.a[2][3] = 0; temp.a[2][4] = 0;
temp.a[3][1] = 1; temp.a[3][2] = 0; temp.a[3][3] = 0; temp.a[3][4] = 1;
temp.a[4][1] = 0; temp.a[4][2] = 0; temp.a[4][3] = 1; temp.a[4][4] = 0;
memset(ans.a, 0, sizeof ans.a);
for(int i = 1; i <= 4; i++) {
ans.a[i][i] = 1;
}
n -= 2;
while(n) {
if(n & 1) {
ans = mul(ans, temp);
}
temp = mul(temp, temp);
n /= 2;
}
long long tot = (ans.a[1][1] * 5 + ans.a[1][2] * 1 + ans.a[1][3] * 1 + ans.a[1][4] * 1) % mod;
//注意dp[0]也要算上并且计为1
printf("%lld\n", tot);
}
return 0;
}