Quad Tiling
poj 3420
题目大意
在一个4×n的棋盘上,用1×2的多米诺骨牌把他填满,问有多少种方法
输入样例
1 10000
3 10000
5 10000
0 0
输出样例
1
11
95
数据范围
1⩽N⩽1091 \leqslant N \leqslant 10^91⩽N⩽109
0<M⩽1050 < M \leqslant 10^50<M⩽105
解题思路
对于放多米诺骨牌可以用状压DP
但直接状压会TLE
考虑矩阵乘法
可以先把状态之间的关系预处理出来
然后快速幂即可
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define wyc mod
using namespace std;
ll n, mod;
struct matrix
{ll n, m, a[20][20];matrix operator *(const matrix &b) const{matrix c;c.n = n;c.m = b.m;for (int i = 1; i <= c.n; ++i)for (int j = 1; j <= c.m; ++j)c.a[i][j] = 0;for (int i = 1; i <= c.n; ++i)for (int k = 1; k <= m; ++k)for (int j = 1; j <= c.m; ++j)c.a[i][j] = (c.a[i][j] + a[i][k] * b.a[k][j] % wyc) % wyc;return c;}
}A, B;
ll ggg(ll x, ll y, ll z, ll g)
{return x + (y<<1) + (z<<2) + (g<<3) + 1;
}
void pp(ll x, ll y, ll z, ll g)
{ll xx = x^1, yy = y^1, zz = z^1, gg = g^1;B.a[ggg(xx, yy, zz, gg)][ggg(x, y, z, g)] = 1;if (!xx && !yy){B.a[ggg(1, 1, zz, gg)][ggg(x, y, z, g)] = 1;if (!zz && !gg)B.a[ggg(1, 1, 1, 1)][ggg(x, y, z, g)] = 1;}if (!yy && !zz)B.a[ggg(xx, 1, 1, gg)][ggg(x, y, z, g)] = 1;if (!zz && !gg)B.a[ggg(xx, yy, 1, 1)][ggg(x, y, z, g)] = 1;return;
}
void Counting(ll x)
{while(x){if (x&1) A = A * B;B = B * B;x>>=1; }return;
}
int main()
{while(1){scanf("%lld%lld", &n, &mod);if (!n) break;for (int i = 1; i <= 16; ++i){A.a[1][i] = 0;for (int j = 1; j <= 16; ++j)B.a[i][j] = 0;}A.n = 1;A.m = B.n = B.m = 16;A.a[1][1] = 1;//初始状态A.a[1][4] = 1;A.a[1][7] = 1;A.a[1][13] = 1;A.a[1][16] = 1;for (int i = 0; i <= 1; ++i)for (int j = 0; j <= 1; ++j)for (int k = 0; k <= 1; ++k)for (int c = 0; c <= 1; ++c)pp(i, j, k, c);//预处理矩阵Counting(n - 1);//快速幂printf("%lld\n", A.a[1][16] % wyc);}return 0;
}