题目描述
金企鹅同学非常擅长用 1×21×21×2 的多米诺骨牌覆盖棋盘的题。有一天,正在背四六级单词的他忽然想:既然两个格子的积木叫“多米诺 (domino)”,那么三个格子的的积木一定叫“三米诺 (tromino)”了!用三米诺覆盖棋盘的题怎么做呢?
用三米诺覆盖 3×n3×n3×n 的矩形棋盘,共多少种方案?三米诺可旋转;两种方案不同当且仅当这两种图案直接覆盖在一起无法重叠。
例如 n=2n=2n=2 时,共 333 种方案:
输入输出格式
输入格式
一行一个整数 n(n≤1040000)n(n≤10^{40000})n(n≤1040000),表示棋盘列数。
输出格式
一行一个整数,表示方案数,对 998244353998244353998244353 取模。
输入输出样例
输入样例#1:
2
输出样例#1:
3
输入样例#2:
3
输出样例#2:
10
输入样例#3:
29
输出样例#3:
543450786
解题分析
我们手玩一波样例, 考虑新一行第iii行从前面某一行完整地转移过来(即是两个矩形拼接到一起的形式)
-
从i−1i-1i−1行转移:只可能是下面这种情况, 所以dp[i]+=dp[i−1]dp[i]+=dp[i-1]dp[i]+=dp[i−1]:
-
从第i−2i-2i−2行转移: 如果放竖着的可以归纳到i−1i-1i−1行中, 我们不再计算。 于是有下面两种情况:
然后我们发现似乎这个可以引出更多的转移:
(对称的并没有画出来)
类似这样的我们同一归到从i−(3n+2)i-(3n+2)i−(3n+2)行转移中, 这样我们可以维护一个sum[3]sum[3]sum[3]记录下标mod 3mod\ 3mod 3分别等于0,1,20,1,20,1,2的答案的前缀和。 这样的话dp[i]+=2×sum[(i−2) mod 3]dp[i]+=2\times sum[(i-2)\ mod \ 3]dp[i]+=2×sum[(i−2) mod 3]。
-
从第i−3i-3i−3行转移, 这样可以引出这样一类, 有四种同构:
即从i−3ni-3ni−3n行转移, 这样的话dp[i]+=sum[i mod 3]dp[i]+=sum[i\ mod\ 3]dp[i]+=sum[i mod 3]
我们发现似乎sum[(i−1) mod 3]sum[(i - 1)\ mod\ 3]sum[(i−1) mod 3]没有用到, 于是再画图就可以发现有这样一种情况:
当然这样也会有对称的情况, 但我们发现从i−1i-1i−1转移的情况也包含在了这个前缀和里面, 而它只能算一次, 所以我们最后需要减掉。
当然从i−3i-3i−3转移还有一种特殊情况:
这个单独算上就好了。
最后的dpdpdp方程就是:
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
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
套上一个矩阵, 十进制快速幂, 完美ACACAC。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cmath>
#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);
}