【题解】Luogu-P5303 [GXOI/GZOI2019]逼死强迫症

P5303 [GXOI/GZOI2019]逼死强迫症

Preface

矩阵题的登峰造极之作。

Description

T T T 组数据。

对于每组数据,给定正整数 N N N,请求出用 ( N − 1 ) (N-1) (N1) 2 × 1 2\times 1 2×1 的方格和 2 2 2 1 × 1 1\times 1 1×1 的方格铺满 2 × N 2\times N 2×N 的大方格的方案数,其中 2 2 2 1 × 1 1\times 1 1×1 的方格不能有相邻的边

  • 答案对 1 0 9 + 7 10^9+7 109+7 取模。
  • 对于 100 % 100\% 100% 的数据, N ≤ 2 × 1 0 9 , T ≤ 500 N\le 2\times 10^9,T\le 500 N2×109,T500

Solution

前置芝士:

  • 一定的小奥基础
  • 矩阵快速幂

第一部分:何为所求

这两个 1 × 1 1\times 1 1×1 一定能框出一个 2 × m 2\times m 2×m 的长方形,其中因为不能相邻,所以 m ≥ 3 m\ge 3 m3

考虑这 2 2 2 1 × 1 1\times 1 1×1 的摆放位置:同行或异行。

举个例子:

m m m 为偶数时:

同行是可以的,而且这个长方形内只有 1 1 1 种方法。

异行是不行的。

m m m 为奇数时:

异行是可以的,而且这个长方形内只有 1 1 1 种方法。

同行是不行的。

综上可以发现无论 m m m 的奇偶性,框出的 2 × m 2\times m 2×m 有且仅有一种方法。

是吗?

因为 2 2 2 个小正方形的位置可以上下调换,所以这两种都需要乘 2 2 2

再考虑全部:

左右两边都是形如 2 × k 2\times k 2×k 的长方形,根据小学奥数可知用 k k k 2 × 1 2\times 1 2×1 的覆盖 2 × k ( k ∈ N ) 2\times k(k\in\mathbf{N}) 2×k(kN) 的有 F k F_k Fk 种(其中 { F k } \{F_k\} {Fk} 为斐波那契数列,下标从 0 0 0 开始)。

确定了两边的 i , j i,j i,j,中间的 m m m 就自动确定了。

中间是 2 2 2 1 × 1 1\times 1 1×1 的框出的长方形,有 2 2 2 种方法,又 m ≥ 3 m\ge 3 m3,所以
a n s = 2 ∑ i = 0 n − 3 ∑ j = 0 n − i − 3 F i ⋅ F j = 2 ∑ i = 0 n − 3 F i ∑ j = 0 n − i − 3 F j \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} ans=2i=0n3j=0ni3FiFj=2i=0n3Fij=0ni3Fj

二、推式子

根据斐波那契数列的前缀和 ∑ i = 0 n F i = F n + 2 − 1 \sum\limits_{i=0}^n F_i=F_{n+2}-1 i=0nFi=Fn+21,有
a n s = 2 ∑ i = 0 n − 3 F i ∑ j = 0 n − i − 3 F j = 2 ∑ i = 0 n − 3 F i ⋅ ( F n − i − 1 − 1 ) = 2 ∑ i = 0 n − 3 ( F i ⋅ F n − i − 1 − F i ) = 2 ( ∑ i = 0 n − 3 F i ⋅ F n − i − 1 − ∑ i = 0 n − 3 F i ) = 2 ( ∑ i = 0 n − 3 F i ⋅ F n − i − 1 − ( F n − 1 − 1 ) ) = 2 ( 1 − F n − 1 + ∑ i = 0 n − 3 F i ⋅ F n − i − 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} ans=2i=0n3Fij=0ni3Fj=2i=0n3Fi(Fni11)=2i=0n3(FiFni1Fi)=2(i=0n3FiFni1i=0n3Fi)=2(i=0n3FiFni1(Fn11))=2(1Fn1+i=0n3FiFni1)
S n = ∑ i = 0 n F n ⋅ F n − i S_n=\sum\limits_{i=0}^n F_n\cdot F_{n-i} Sn=i=0nFnFni,则
a n s = 2 ( 1 − F n − 1 + ∑ i = 0 n − 3 F i ⋅ F n − i − 1 ) = 2 [ 1 − F n − 1 + ( ∑ i = 0 n − 1 F i ⋅ F n − i − 1 − F n − 2 ⋅ F 1 − F n − 1 ⋅ F 0 ) ] = 2 ( 1 + S n − 1 − 2 F n − 1 − F n − 2 ) \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} ans=2(1Fn1+i=0n3FiFni1)=2[1Fn1+(i=0n1FiFni1Fn2F1Fn1F0)]=2(1+Sn12Fn1Fn2)
现在的问题就是如何求出 S n S_n Sn
S n = ∑ i = 0 n F n ⋅ F n − i = F n ⋅ F 0 + F n − 1 ⋅ F 1 + ∑ i = 0 n − 2 F i ⋅ F n − i = F n + F n − 1 + ∑ i = 0 n − 2 F i ⋅ ( F n − i − 1 + F n − i − 2 ) = F n + ( F n − 1 ⋅ F 0 + ∑ i = 0 n − 2 F i ⋅ F n − i − 1 ) + ∑ i = 0 n − 2 F i ⋅ F n − i − 2 = F n + ∑ i = 0 n − 1 F i ⋅ F n − i − 1 + ∑ i = 0 n − 2 F i ⋅ F n − i − 2 = F n − 1 + F n − 2 + S n − 1 + S n − 2 \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} Sn=i=0nFnFni=FnF0+Fn1F1+i=0n2FiFni=Fn+Fn1+i=0n2Fi(Fni1+Fni2)=Fn+(Fn1F0+i=0n2FiFni1)+i=0n2FiFni2=Fn+i=0n1FiFni1+i=0n2FiFni2=Fn1+Fn2+Sn1+Sn2
可以用矩阵快速幂解决:
{ S n = 1 ⋅ S n − 1 + 1 ⋅ S n − 2 + 1 ⋅ F n − 1 + 1 ⋅ F n − 2 S n − 1 = 1 ⋅ S n − 1 + 0 ⋅ S n − 2 + 0 ⋅ F n − 1 + 0 ⋅ F n − 2 F n = 0 ⋅ S n − 1 + 0 ⋅ S n − 2 + 1 ⋅ F n − 1 + 1 ⋅ F n − 2 F n − 1 = 0 ⋅ S n − 1 + 0 ⋅ S n − 2 + 1 ⋅ F n − 1 + 0 ⋅ F n − 2 \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} SnSn1FnFn1=1Sn1+1Sn2+1Fn1+1Fn2=1Sn1+0Sn2+0Fn1+0Fn2=0Sn1+0Sn2+1Fn1+1Fn2=0Sn1+0Sn2+1Fn1+0Fn2
那么矩阵就是
[ 1 1 1 1 1 0 0 0 0 0 1 1 0 0 1 0 ] × [ S n − 1 S n − 2 F n − 1 F n − 2 ] = [ S n S n − 1 F n F n − 1 ] \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} 1100100010111010×Sn1Sn2Fn1Fn2=SnSn1FnFn1
初始值为
[ S 1 S 0 F 1 F 0 ] = [ 2 1 1 1 ] \begin{bmatrix} S_1\\ S_0\\ F_1\\ F_0 \end{bmatrix}= \begin{bmatrix} 2\\ 1\\ 1\\ 1 \end{bmatrix} S1S0F1F0=2111
a n s = 2 ( 1 + S n − 1 − 2 F n − 1 − F n − 2 ) ans=2(1+S_{n-1}-2F_{n-1}-F_{n-2}) ans=2(1+Sn12Fn1Fn2),所以求 S n − 1 S_{n-1} Sn1 ( n − 2 ) (n-2) (n2) 次转移矩阵即可。

时间复杂度为 O ( T ⋅ 64 log ⁡ n ) O(T\cdot 64\log n) O(T64logn)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值