NC19833 地斗主(矩阵快速幂)

题目链接

题意:
有 一 个 4 ∗ n 的 网 格 有一个4*n的网格 4n
有 无 数 个 1 ∗ 2 的 骨 牌 , 可 以 旋 转 有无数个1*2的骨牌,可以旋转 12
问 把 他 铺 满 网 格 有 多 少 方 案 数 问把他铺满网格有多少方案数
题解:
首 先 我 们 假 设 f ( n ) 表 示 4 ∗ n 网 格 的 方 案 数 首先我们假设f(n)表示4*n网格的方案数 f(n)4n
那 么 可 以 发 现 , 他 是 通 过 前 几 个 补 充 转 移 来 的 那么可以发现,他是通过前几个补充转移来的
那 么 就 有 f n = ∑ i = 1 n − 1 a i ∗ f n − i 那么就有f_n=\sum^{n - 1}_{i=1}a_i*f_{n - i} fn=i=1n1aifni
但 是 a i 需 要 去 找 一 下 规 律 , 因 为 可 能 a n 和 a n − 1 会 有 重 复 但是a_i需要去找一下规律,因为可能a_n和a_{n-1}会有重复 aianan1
然 后 手 画 出 来 几 个 可 以 发 现 , 其 实 就 是 对 4 ∗ 1 , 4 ∗ 2 然后手画出来几个可以发现,其实就是对4*1,4*2 41,42
类 似 这 些 进 行 补 充 , 去 除 重 复 的 类似这些进行补充,去除重复的
a 1 = 1 , a 2 = 4 , a 3 = 2 , a 4 = 3 , a 5 = 2 , a 6 = 3 a_1=1,a_2=4,a_3=2,a_4=3,a_5=2,a_6=3 a1=1,a2=4,a3=2,a4=3,a5=2,a6=3
会 发 现 a 1 = 1 , a 2 = 4 , n < = 2 会发现a_1=1,a_2=4, n<=2 a1=1,a2=4,n<=2
a i = 2 , n % 2 = = 1 a_i=2,n \% 2 ==1 ai=2,n%2==1
a i = 3 , n % 2 = = 0 a_i=3,n\%2==0 ai=3,n%2==0
那 么 公 式 就 可 以 改 为 那么公式就可以改为
f n = f n − 1 + 4 f n − 2 + 2 f n − 3 + 3 f n − 4 + … … f_n=f_{n-1}+4f_{n-2}+2f_{n-3}+3f_{n-4}+…… fn=fn1+4fn2+2fn3+3fn4+
然 后 f n − 2 = f n − 3 + 4 f n − 4 + 2 f n − 5 + 3 f n − 6 然后f_{n-2}=f_{n-3}+4f_{n-4}+2f_{n-5}+3f_{n-6} fn2=fn3+4fn4+2fn5+3fn6
那 么 式 子 就 成 了 f n = f n − 1 + 5 f n − 2 + f n − 3 − f n − 4 那么式子就成了f_n=f_{n-1}+5f_{n-2}+f_{n-3}-f_{n-4} fn=fn1+5fn2+fn3fn4
公 式 一 有 , 很 明 显 是 个 线 性 公 式 , 那 么 直 接 用 矩 阵 快 速 幂 列 式 解 决 公式一有,很明显是个线性公式,那么直接用矩阵快速幂列式解决 线
AC代码

/*
    Author : zzugzx
    Lang : C++
    Blog : blog.csdn.net/qq_43756519
*/
#include
using namespace std;

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
#define mem(a, b) memset(a, b, sizeof(a))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod = 1e9 + 7;
//const int mod = 998244353;

const double eps = 1e-6;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;
const int N = 1e2 + 5;
const ll inf = 0x3f3f3f3f;
const int dir[][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int mod;
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
ll inv(ll b){
    return Pow(b,mod-2)%mod;
}
class mat {
public:
    int n,m;
    ll v[N][N], is[N], js[N];
    mat(int n,int m) : n(n), m(m){ memset(v, 0, sizeof(v));}
    void init() {
        memset(v, 0, sizeof(v));
    }
    void init1() {
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                v[i][j] = (i == j); //单位矩阵
    }
    mat operator * (const mat B) const {//矩阵乘法 A(n,k)*B(k,m)=C(n,m);
        mat C(n, B.m);
        C.init();
        for(int i = 0; i < n; i++)
        for(int j = 0; j < B.m; j++)
        for(int k = 0; k < m; k++)
            C.v[i][j] = (C.v[i][j]+v[i][k]*B.v[k][j]) % mod;//Mod
        return C;
    }
    mat operator ^ (int t)//矩阵快速幂 n=m时可用
    {
        mat ans(n, n), now(n, n);
        ans.init1();
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                now.v[i][j] = v[i][j];
        while(t > 0) {
            if(t & 1) ans = ans * now;
            now = now * now;
            t >>= 1;
        }
        return ans;
    }
    void change() { // 转置
        swap(n, m);
        for(int i = 0; i < max(n, m); i++) {
            for(int j = i + 1; j < max(n, m); j++) {
                swap(v[i][j], v[j][i]);
            }
        }
    }
    void Minv() {  // 逆矩阵
        for(int k = 0; k < n; k++) {
            for(int i = k; i < n; i++) // 1
                for(int j = k; j < n; j++)
                    if(v[i][j]) {
                        is[k] = i;
                        js[k] = j;
                        break;
                    }
            for(int i = 0; i < n; i++) // 2
                swap(v[k][i], v[is[k]][i]);
            for(int i = 0; i < n; i++)
                swap(v[i][k], v[i][js[k]]);
            v[k][k] = inv(v[k][k]); // 3
            for(int j = 0; j < n; j++)
                if(j != k) // 4
                    v[k][j] = v[k][j] * v[k][k] % mod;
            for(int i = 0; i < n; i++)
                if(i != k) // 5
                for(int j = 0; j < n; j++)
                    if(j != k)
                        v[i][j] = (v[i][j] + mod - v[i][k] * v[k][j] % mod) % mod;
            for(int i = 0; i < n; i++)
                if(i != k)
                v[i][k] = (mod - v[i][k] * v[k][k] % mod) % mod;
        }
        for(int k = n - 1; k >= 0; k--) { // 6
            for(int i = 0; i < n; i++)
                swap(v[js[k]][i], v[k][i]);
            for(int i = 0; i < n; i++)
                swap(v[i][is[k]], v[i][k]);
        }
    }
};
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
//  freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    int _;
    cin >> _;
    while (_--) {
        mat A(4, 1), B(4, 4);
        B.v[0][0] = B.v[0][2] = 1;
        B.v[0][1] = 5, B.v[0][3] = -1;
        B.v[1][0] = B.v[2][1] = B.v[3][2] = 1;
        A.v[0][0] = 36, A.v[1][0] = 11;
        A.v[2][0] = 5, A.v[3][0] = 1;
        int n;
        cin >> n >> mod;
        if (n <= 4) cout << A.v[4 - n][0] << endl;
        else {
            mat C = (B ^ (n - 4)) * A;
            cout << (C.v[0][0] + mod) % mod << endl;
        }
    }
    return 0;
}

你可能感兴趣的:(NC19833 地斗主(矩阵快速幂))