[2018.10.10 T3] 三米诺

探讨使用三米诺(tromino)积木覆盖3×n的矩形棋盘问题,介绍题目的背景、描述及解决思路。文章分享了一种递推公式求解方案数的方法,并提供了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂无链接

三米诺

题目背景

金企鹅同学非常擅长用1×21 × 21×2的多米诺骨牌覆盖棋盘的题。有一天,正在背四六级单词的他忽然想:既然两个格子的积木叫“多米诺 (domino)”,那么三个格子的的积木一定叫“三米诺 (tromino)”了!用三米诺覆盖棋盘的题怎么做呢?

题目描述

用三米诺覆盖3×n3 × n3×n的矩形棋盘,共多少种方案?三米诺可旋转;两种方案不同当且仅当这两种图案直接覆盖在一起无法重叠。

例如n=2n = 2n=2时,共333种方案:

1.png

用三米诺覆盖3×2棋盘用三米诺覆盖3\times 2棋盘3×2

格式
输入格式

一行一个整数n(n≤1040000)n(n ≤ 10^{40000})n(n1040000),表示棋盘列数。

输出格式

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

样例
样例 1 输入

2

样例 1 输出

3

样例 2 输入

3

样例 2 输出

10

样例 3 输入

29

样例 3 输出

543450786

数据范围

对于10%10\%10%的数据,n≤5n ≤ 5n5
对于30%30\%30%的数据,n≤106n ≤ 10^6n106
对于40%40\%40%的数据,n≤20001000n ≤ 20001000n20001000
对于60%60\%60%的数据,n≤109n ≤ 10^9n109
对于80%80\%80%的数据,n≤101000n ≤ 10^{1000}n101000
对于100%100\%100%的数据,n≤1040000n ≤ 10^{40000}n1040000

题解

借鉴多米诺的思路,我们很容易想到长度为1,2,31,2,31,2,3的转移,然而三米诺的精髓在于存在弯折的形状,这使三米诺可以将几个长条错起来放,不需要像多米诺一样长条必须并排放置。

于是出现了下面三种奇妙的转移:
1.png
2.png
3.png

你会发现它们都是可以无限延伸且不重不漏的,要dpdpdp统计答案的话,可能是O(n2)O(n^2)O(n2)的。。。

这个时候你可以暴力算前几项,然后 上OEIS通过高斯消元把递推系数解出来,得到正确的递推式:

4.png
论一个熟练的OEISer如何快速AC此题。

神犇Rockud\mathcal{Rockud}Rockud还有一个牛逼哄哄的维护了三个前缀和的递推做法,作为蒟蒻只能%%%\%\%\%%%%

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=4e5,N=6,mod=998244353;
struct sd{ll sq[N+2][N+2];}c,one,base,ans,ni,r;
int mat[N+1][N+1]={0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,2,0,1,0,0,0,0,6,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,0,0,0,0,0};
int fan[N+1][N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,0,0,1,0,0,1,0,0,0,2,0,0,0,1,0,0,6,0,0,0,0,1,0,1,0,0,0,0,0,1,0};
int f[]={1,1,3,10,23,62,170};
ll AC,len;
char num[M];
sd operator*(sd a,sd b){for(int i=1,j,k;i<=N;++i)for(j=1;j<=N;++j)for(c.sq[i][j]=0,k=1;k<=N;++k)(c.sq[i][j]+=a.sq[i][k]*b.sq[k][j]%mod)%=mod;return c;}
sd power(sd a,int p){for(r=one;p;p>>=1,a=a*a)if(p&1)r=r*a;return r;}
void in(){scanf("%s",num+1);}
void ac()
{
	if((len=strlen(num+1))==1&&num[1]-'0'<=6){printf("%d",f[num[1]-'0']);return;}
	for(int i=1;i<=N;++i)ans.sq[i][i]=one.sq[i][i]=1;
	for(int i=1;i<=N;++i)for(int j=1;j<=N;++j)base.sq[i][j]=mat[i][j],ni.sq[i][j]=fan[i][j];
	for(int i=len;i;--i)ans=ans*power(base,num[i]-'0'),base=power(base,10);
	ans=ans*power(ni,6);
	for(int i=1;i<=N;++i)(AC+=ans.sq[i][1]*f[N-i+1]%mod)%=mod;
	printf("%lld\n",(AC+mod)%mod);
}
int main(){in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值