金企鹅同学非常擅长用 1 × 2 1×2 1×2 的多米诺骨牌覆盖棋盘的题。有一天,正在背四六级单词的他忽然想:既然两个格子的积木叫“多米诺 (domino)”,那么三个格子的的积木一定叫“三米诺 (tromino)”了!用三米诺覆盖棋盘的题怎么做呢?
用三米诺覆盖 3 × n 3×n 3×n 的矩形棋盘,共多少种方案?三米诺可旋转;两种方案不同当且仅当这两种图案直接覆盖在一起无法重叠。
例如 n = 2 n=2 n=2 时,共 3 3 3 种方案:
一行一个整数 n ( n ≤ 1 0 40000 ) n(n≤10^{40000}) n(n≤1040000),表示棋盘列数。
一行一个整数,表示方案数,对 998244353 998244353 998244353 取模。
2
3
3
10
29
543450786
我们手玩一波样例, 考虑新一行第 i i i行从前面某一行完整地转移过来(即是两个矩形拼接到一起的形式)
从 i − 1 i-1 i−1行转移:只可能是下面这种情况, 所以 d p [ i ] + = d p [ i − 1 ] dp[i]+=dp[i-1] dp[i]+=dp[i−1]:
从第 i − 2 i-2 i−2行转移: 如果放竖着的可以归纳到 i − 1 i-1 i−1行中, 我们不再计算。 于是有下面两种情况:
(对称的并没有画出来)
类似这样的我们同一归到从 i − ( 3 n + 2 ) i-(3n+2) i−(3n+2)行转移中, 这样我们可以维护一个 s u m [ 3 ] sum[3] sum[3]记录下标 m o d 3 mod\ 3 mod 3分别等于 0 , 1 , 2 0,1,2 0,1,2的答案的前缀和。 这样的话 d p [ i ] + = 2 × s u m [ ( i − 2 ) m o d 3 ] dp[i]+=2\times sum[(i-2)\ mod \ 3] dp[i]+=2×sum[(i−2) mod 3]。
即从 i − 3 n i-3n i−3n行转移, 这样的话 d p [ i ] + = s u m [ i m o d 3 ] dp[i]+=sum[i\ mod\ 3] dp[i]+=sum[i mod 3]
我们发现似乎 s u m [ ( i − 1 ) m o d 3 ] sum[(i - 1)\ mod\ 3] sum[(i−1) mod 3]没有用到, 于是再画图就可以发现有这样一种情况:
当然这样也会有对称的情况, 但我们发现从 i − 1 i-1 i−1转移的情况也包含在了这个前缀和里面, 而它只能算一次, 所以我们最后需要减掉。
这个单独算上就好了。
最后的 d p dp dp方程就是:
d p [ i ] = − d p [ i − 1 ] + s u m [ ( i − 1 ) m o d 3 ] × 2 + d p [ i − 3 ] + s u m [ i m o d 3 ] × 4 + s u m [ ( i − 2 ) m o d 3 ] × 2 dp[i]=-dp[i-1]+sum[(i-1)\ mod\ 3]\times 2+dp[i-3]+sum[i\ mod\ 3]\times 4+sum[(i-2)\ mod\ 3]\times 2 dp[i]=−dp[i−1]+sum[(i−1) mod 3]×2+dp[i−3]+sum[i mod 3]×4+sum[(i−2) mod 3]×2
套上一个矩阵, 十进制快速幂, 完美 A C AC AC。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MOD 998244353
struct Matrix
{
ll mat[6][6];
void clear() {std::memset(mat, 0, sizeof(mat));}
}base, res, ans;
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret; ret.clear();
for (R int i = 0; i < 6; ++i)
for (R int j = 0; j < 6; ++j)
for (R int k = 0; k < 6; ++k)
ret.mat[i][k] = ret.mat[i][k] + 1ll * x.mat[i][j] * y.mat[j][k];
for (R int i = 0; i < 6; ++i)
for (R int j = 0; j < 6; ++j) ret.mat[i][j] %= MOD;
return ret;
}
char buf[40050];
int len;
IN void True_fpow(Matrix &tar, Matrix res, R int tm)
{
W (tm)
{
if(tm & 1) tar = tar * res;
res = res * res; tm >>= 1;
}
}
IN void fpow()
{
Matrix emp = base, unit = base;
for (R int i = len; i; --i)
{
True_fpow(base, res, buf[i] - '0');
unit = emp;
True_fpow(unit, res, 10);
res = unit;
}
}
void dealit()
{
R int pos = len;
if(buf[pos] >= '2') return buf[pos] -= 2, void();
--pos; W (buf[pos] == '0') --pos;
--buf[pos]; for (R int i = pos + 1; i < len; ++i) buf[i] = '9';
buf[len] += 8;
}
int main(void)
{
res = (Matrix){ -1, 0, 1, 4, 2, 2,//转移矩阵
1, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 1,
-1, 0, 1, 5, 2, 2};
base = (Matrix){1, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 1};
//初始状态
ans = (Matrix){ 3, 0, 0, 0, 0, 0,//dp[2]
1, 0, 0, 0, 0, 0,//dp[1]
1, 0, 0, 0, 0, 0,//dp[0]
1, 0, 0, 0, 0, 0,//sum[0]
1, 0, 0, 0, 0, 0,//sum[1]
3, 0, 0, 0, 0, 0};//sum[2]
scanf("%s", buf + 1); len = std::strlen(buf + 1);
if(len == 1 && buf[1] == '1') return puts("1"), 0;
if(len == 1 && buf[1] == '2') return puts("3"), 0;
dealit();
fpow();
printf("%d", ((base * ans).mat[0][0] + MOD) % MOD);
}