九校联考-DL24 凉心模拟 Day1T3 三米诺 (tromino)

题目描述

金企鹅同学非常擅长用 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(n1040000),表示棋盘列数。

输出格式

一行一个整数,表示方案数,对 998244353 998244353 998244353 取模。

输入输出样例

输入样例#1:
2
输出样例#1:
3
输入样例#2:
3
输出样例#2:
10
输入样例#3:
29
输出样例#3:
543450786

解题分析

我们手玩一波样例, 考虑新一行第 i i i行从前面某一行完整地转移过来(即是两个矩形拼接到一起的形式)

  • i − 1 i-1 i1行转移:只可能是下面这种情况, 所以 d p [ i ] + = d p [ i − 1 ] dp[i]+=dp[i-1] dp[i]+=dp[i1]

  • 从第 i − 2 i-2 i2行转移: 如果放竖着的可以归纳到 i − 1 i-1 i1行中, 我们不再计算。 于是有下面两种情况:

    然后我们发现似乎这个可以引出更多的转移:

    (对称的并没有画出来)

    类似这样的我们同一归到从 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[(i2) mod 3]

  • 从第 i − 3 i-3 i3行转移, 这样可以引出这样一类, 有四种同构:

即从 i − 3 n i-3n i3n行转移, 这样的话 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[(i1) mod 3]没有用到, 于是再画图就可以发现有这样一种情况:

当然这样也会有对称的情况, 但我们发现从 i − 1 i-1 i1转移的情况也包含在了这个前缀和里面, 而它只能算一次, 所以我们最后需要减掉。

当然从 i − 3 i-3 i3转移还有一种特殊情况:

这个单独算上就好了。

最后的 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[i1]+sum[(i1) mod 3]×2+dp[i3]+sum[i mod 3]×4+sum[(i2) mod 3]×2
套上一个矩阵, 十进制快速幂, 完美 A C AC AC

代码如下:

#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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值