1. 程式人生 > >九校聯考-DL24 涼心模擬 Day1T3 三米諾 (tromino)

九校聯考-DL24 涼心模擬 Day1T3 三米諾 (tromino)

題目描述

金企鵝同學非常擅長用 1×21×2 的多米諾骨牌覆蓋棋盤的題。有一天,正在背四六級單詞的他忽然想:既然兩個格子的積木叫“多米諾 (domino)”,那麼三個格子的的積木一定叫“三米諾 (tromino)”了!用三米諾覆蓋棋盤的題怎麼做呢?

用三米諾覆蓋 3×n3×n 的矩形棋盤,共多少種方案?三米諾可旋轉;兩種方案不同當且僅當這兩種圖案直接覆蓋在一起無法重疊。

例如 n=2n=2 時,共 33 種方案:

輸入輸出格式

輸入格式

一行一個整數 n(n1040000)n(n≤10^{40000}),表示棋盤列數。

輸出格式

一行一個整數,表示方案數,對 998244353

998244353 取模。

輸入輸出樣例

輸入樣例#1:

2

輸出樣例#1:

3

輸入樣例#2:

3

輸出樣例#2:

10

輸入樣例#3:

29

輸出樣例#3:

543450786

解題分析

我們手玩一波樣例, 考慮新一行第ii行從前面某一行完整地轉移過來(即是兩個矩形拼接到一起的形式)

  • i1i-1行轉移:只可能是下面這種情況, 所以dp[i]+=dp[i1]dp[i]+=dp[i-1]

  • 從第i2i-2行轉移: 如果放豎著的可以歸納到i1i-1行中, 我們不再計算。 於是有下面兩種情況:

    然後我們發現似乎這個可以引出更多的轉移:

    (對稱的並沒有畫出來)

    類似這樣的我們同一歸到從i(3n+2)i-(3n+2)行轉移中, 這樣我們可以維護一個sum[3]sum[3]記錄下標mod3mod\ 3分別等於0,1,20,1,2的答案的字首和。 這樣的話dp[i]+=2×sum[(i2)mod3]dp[i]+=2\times sum[(i-2)\ mod \ 3]

  • 從第i3i-3行轉移, 這樣可以引出這樣一類, 有四種同構:

即從i3ni-3n行轉移, 這樣的話dp[i]+=sum[imod3]dp[i]+=sum[i\ mod\ 3]

我們發現似乎sum[(i1)mod3]sum[(i - 1)\ mod\ 3]沒有用到, 於是再畫圖就可以發現有這樣一種情況:

當然這樣也會有對稱的情況, 但我們發現從i1i-1轉移的情況也包含在了這個字首和裡面, 而它只能算一次, 所以我們最後需要減掉。

當然從i3i-3轉移還有一種特殊情況:

這個單獨算上就好了。

最後的dpdp方程就是: dp[i]=dp[i1]+sum[(i1)mod3]×2+dp[i3]+sum[imod3]×4+sum[(i2)mod3]×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 套上一個矩陣, 十進位制快速冪, 完美ACAC

程式碼如下:

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