Quad Tiling POJ - 3420 (矩阵快速幂)题解


Tired of the Tri Tiling game finally, Michael turns to a more challengeable game, Quad Tiling:

In how many ways can you tile a 4 × N (1 ≤ N ≤ 109) rectangle with 2 × 1 dominoes? For the answer would be very big, output the answer modulo M (0 < M ≤ 105).

Input
Input consists of several test cases followed by a line containing double 0. Each test case consists of two integers, N and M, respectively.

Output
For each test case, output the answer modules M.

Sample Input
1 10000
3 10000
5 10000
0 0
Sample Output
1
11
95


由Tri Tiling引申出来的题目,问在4*n的长方形中摆放1*2的方块的方案数,很明显的递推题,在纸上大概画一画就能把几种情形推出来,得到公式f[n]=f[n-1]+4f[n-2]+2(f[n-3]+f[n-4]+f[n-5]+…..+f[0])+3(f[n-6]+f[n-8]+…..),首先因为n的大小太大了,想这样递推数组都存不下,时间也不够,就应该用矩阵快速幂计算,而如果想用矩阵快速幂的话这个公式是不满足的,因为矩阵大小不固定,然而当我们将f[n]-f[n-2]之后,就会得到f[n]=f[n-1]+5f[n-2]+f[n-3]-f[n-4],就能得到快速幂的矩阵,套入计算就行。因为矩阵里有负数,计算一个位置的结果时,前面的正数取模变小了,后面的负数没达到模数那么大,结果相加就变成负数了。所以矩阵相乘时注意要+mod)%mod。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
//const int mod = 1000000007;
const int maxm = 1000010;
const int maxn = 1000000005;
int n;
int m;
struct ju{
    ll a[4][4];
    ju(){}
    ju(ll *num){
        int cnt = 0;
        for (int i = 0; i < 4; i++){
            for (int j = 0; j < 4; j++){
                a[i][j] = num[cnt++];
            }
        }
    }
    ju operator *(const ju & tmp){
        ju ans;
        memset(ans.a, 0, sizeof(ans.a));
        for (int i = 0; i<4; i++){
            for (int j = 0; j<4; j++){
                for (int k = 0; k<4; k++){
                    ans.a[i][j] = ((ans.a[i][j] +( a[i][k] * tmp.a[k][j])%m)+m) % m;
                }
            }
        }
        return ans;
    }
};


int pow_mod(int b){
    ll num[16] = { 1, 5, 1, -1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 };
    ll nu[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
    ju a(num);
    ju c(nu);
    while (b){
        if (b % 2)c = c*a;
        a = a*a;
        b /= 2;
    }
    int ans = 0;
    ans = (ans + c.a[0][0] * 11) % m;
    ans = (ans + c.a[0][1] * 5) % m;
    ans = (ans + c.a[0][2] * 1) % m;
    ans = (ans + c.a[0][3]) % m;
    return ans;
}
//1 1 5 11
int f[4] = { 1, 1, 5, 11 };
int main() {
    while (scanf("%d%d", &n, &m), m + n){
        if (n < 4){
            printf("%d\n", f[n] % m);
        }
        else{
            printf("%d\n", pow_mod(n - 3));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值