1. 程式人生 > 其它 >【題解】Luogu-P5303 [GXOI/GZOI2019]逼死強迫症

【題解】Luogu-P5303 [GXOI/GZOI2019]逼死強迫症

P5303 [GXOI/GZOI2019]逼死強迫症

Preface

矩陣題的登峰造極之作。

Description

\(T\) 組資料。

對於每組資料,給定正整數 \(N\),請求出用 \((N-1)\)\(2\times 1\) 的方格和 \(2\)\(1\times 1\) 的方格鋪滿 \(2\times N\) 的大方格的方案數,其中 \(2\)\(1\times 1\) 的方格不能有相鄰的邊

  • 答案對 \(10^9+7\) 取模。
  • 對於 \(100\%\) 的資料,\(N\le 2\times 10^9,T\le 500\)

Solution

前置芝士:

  • 一定的小奧基礎
  • 矩陣快速冪

第一部分:何為所求

這兩個 \(1\times 1\) 一定能框出一個 \(2\times m\) 的長方形,其中因為不能相鄰,所以 \(m\ge 3\)

考慮這 \(2\)\(1\times 1\) 的擺放位置:同行或異行。

舉個例子:

\(m\) 為偶數時:

同行是可以的,而且這個長方形內只有 \(1\) 種方法。

異行是不行的。

\(m\) 為奇數時:

異行是可以的,而且這個長方形內只有 \(1\) 種方法。

同行是不行的。

綜上可以發現無論 \(m\) 的奇偶性,框出的 \(2\times m\) 有且僅有一種方法。

是嗎?

因為 \(2\) 個小正方形的位置可以上下調換,所以這兩種都需要乘 \(2\)

再考慮全部:

左右兩邊都是形如 \(2\times k\) 的長方形,根據小學奧數可知用 \(k\)\(2\times 1\) 的覆蓋 \(2\times k(k\in\mathbf{N})\) 的有 \(F_k\) 種(其中 \(\{F_k\}\) 為斐波那契數列,定義 \(F_0=1\))。

確定了兩邊的 \(i,j\),中間的 \(m\) 就自動確定了。

中間是 \(2\)\(1\times 1\) 的框出的長方形,有 \(2\) 種方法,又 \(m\ge 3\),所以

\[\begin{aligned} ans &=2\sum\limits_{i=0}^{n-3}\sum\limits_{j=0}^{n-i-3} F_i\cdot F_j\\ &=2\sum\limits_{i=0}^{n-3}F_i\sum\limits_{j=0}^{n-i-3}F_j \end{aligned} \]

根據斐波那契數列的字首和 \(\sum\limits_{i=0}^n F_i=F_{i+2}-1\)

,有

\[\begin{aligned} ans &=2\sum\limits_{i=0}^{n-3}F_i\sum\limits_{j=0}^{n-i-3}F_j\\ &=2\sum\limits_{i=0}^{n-3}F_i\cdot (F_{n-i-1}-1)\\ &=2\sum\limits_{i=0}^{n-3}(F_i\cdot F_{n-i-1}-F_i)\\ &=2(\sum\limits_{i=0}^{n-3}F_i\cdot F_{n-i-1}-\sum\limits_{i=0}^{n-3}F_i)\\ &=2(\sum\limits_{i=0}^{n-3}F_i\cdot F_{n-i-1}-(F_{n-1}-1))\\ &=2(1-F_{n-1}+\sum\limits_{i=0}^{n-3}F_i\cdot F_{n-i-1}) \end{aligned} \]

\(S_n=\sum\limits_{i=0}^n F_n\cdot F_{n-i}\),則

\[\begin{aligned} ans &=2(1-F_{n-1}+\sum\limits_{i=0}^{n-3}F_i\cdot F_{n-i-1})\\ &=2[1-F_{n-1}+(\sum\limits_{i=0}^{n-1}F_i\cdot F_{n-i-1}-F_{n-2}\cdot F_1-F_{n-1}\cdot F_0)]\\ &=2(1+S_{n-1}-2F_{n-1}-F_{n-2}) \end{aligned} \]

現在的問題就是如何求出 \(S_n\)

\[\begin{aligned} S_n &=\sum\limits_{i=0}^n F_n\cdot F_{n-i}\\ &=F_n\cdot F_0+F_{n-1}\cdot F_1+\sum\limits_{i=0}^{n-2}F_i\cdot F_{n-i}\\ &=F_n+F_{n-1}+\sum\limits_{i=0}^{n-2}F_i\cdot (F_{n-i-1}+F_{n-i-2})\\ &=F_n+(F_{n-1}\cdot F_0+\sum\limits_{i=0}^{n-2}F_i\cdot F_{n-i-1})+\sum\limits_{i=0}^{n-2}F_i\cdot F_{n-i-2}\\ &=F_n+\sum\limits_{i=0}^{n-1}F_i\cdot F_{n-i-1}+\sum\limits_{i=0}^{n-2}F_i\cdot F_{n-i-2}\\ &=F_{n-1}+F_{n-2}+S_{n-1}+S_{n-2} \end{aligned} \]

可以用矩陣快速冪解決:

\[\begin{cases} S_n&=1\cdot S_{n-1}+1\cdot S_{n-2}+1\cdot F_{n-1}+1\cdot F_{n-2}\\ S_{n-1}&=1\cdot S_{n-1}+0\cdot S_{n-2}+0\cdot F_{n-1}+0\cdot F_{n-2}\\ F_{n}&=0\cdot S_{n-1}+0\cdot S_{n-2}+1\cdot F_{n-1}+1\cdot F_{n-2}\\ F_{n-1}&=0\cdot S_{n-1}+0\cdot S_{n-2}+1\cdot F_{n-1}+0\cdot F_{n-2} \end{cases} \]

那麼矩陣就是

\[\begin{bmatrix} 1 & 1 & 1 & 1\\ 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 1\\ 0 & 0 & 1 & 0 \end{bmatrix} \times \begin{bmatrix} S_{n-1}\\ S_{n-2}\\ F_{n-1}\\ F_{n-2} \end{bmatrix} = \begin{bmatrix} S_n\\ S_{n-1}\\ F_n\\ F_{n-1} \end{bmatrix} \]

初始值為

\[\begin{bmatrix} S_1\\ S_0\\ F_1\\ F_0 \end{bmatrix} = \begin{bmatrix} 2\\ 1\\ 1\\ 1 \end{bmatrix} \]

\(ans=2(1+S_{n-1}-2F_{n-1}-F_{n-2})\),所以求 \(S_{n-1}\)\((n-2)\) 次即可。

Code

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <cstring>
#define Debug(x) cout << #x << "=" << x << endl
#define int long long
using namespace std;

struct matrix
{
	int a[5][5];
	matrix()
	{
		memset(a, 0, sizeof(a));
	}
	void build()
	{
		for (int i = 1; i <= 4; i++)
		{
			a[i][i] = 1;
		}
	}
};

const int MOD = 1e9 + 7;

matrix operator *(matrix x, matrix y)
{
	matrix res;
	for (int k = 1; k <= 4; k++)
	{
		for (int i = 1; i <= 4; i++)
		{
			if (!x.a[i][k])
			{
				continue;
			}
			for (int j = 1; j <= 4; j++)
			{
				res.a[i][j] = (res.a[i][j] + x.a[i][k] * y.a[k][j] % MOD) % MOD;
			}
		}
	}
	return res;
}

matrix qpow(matrix a, int b)
{
	matrix base = a, ans;
	ans.build();
	while (b)
	{
		if (b & 1)
		{
			ans = ans * base;
		}
		base = base * base;
		b >>= 1;
	}
	return ans;
}

signed main()
{
	int t;
	scanf("%lld", &t);
	matrix base;
	base.a[1][1] = base.a[1][2] = base.a[1][3] = base.a[1][4] = base.a[2][1] = base.a[3][3] = base.a[3][4] = base.a[4][3] = 1; //轉移矩陣
	matrix fst;
	fst.a[1][1] = 2, fst.a[2][1] = fst.a[3][1] = fst.a[4][1] = 1; //初始矩陣
	while (t--)
	{
		int n;
		scanf("%lld", &n);
		if (n == 1)
		{
			puts("0");
			continue;
		}
		matrix res = qpow(base, n - 2) * fst;
		int S = res.a[1][1], F1 = res.a[3][1], F2 = res.a[4][1]; //S : S_{n-1}, F1 : F_{n-1}, F2 : F_{n-2}
		printf("%lld\n", (((1 + S - (F1 << 1) - F2) << 1) % MOD + MOD) % MOD);
	}
	return 0;
}

Reference