[2018.10.10 T3] 三米诺

暂无链接

三米诺

题目背景

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

题目描述

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

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

[2018.10.10 T3] 三米诺

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

格式
输入格式

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

输出格式

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

样例
样例 1 输入

2

样例 1 输出

3

样例 2 输入

3

样例 2 输出

10

样例 3 输入

29

样例 3 输出

543450786

数据范围

对于10%10\%的数据,n5n ≤ 5
对于30%30\%的数据,n106n ≤ 10^6
对于40%40\%的数据,n20001000n ≤ 20001000
对于60%60\%的数据,n109n ≤ 10^9
对于80%80\%的数据,n101000n ≤ 10^{1000}
对于100%100\%的数据,n1040000n ≤ 10^{40000}

题解

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

于是出现了下面三种奇妙的转移:
[2018.10.10 T3] 三米诺
[2018.10.10 T3] 三米诺
[2018.10.10 T3] 三米诺

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

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

[2018.10.10 T3] 三米诺
论一个熟练的OEISer如何快速AC此题。

神犇Rockud\mathcal{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();}