【详●析】[GXOI/GZOI2019]逼死强迫症

【题目大意】

\(2\times N\)的方格中用\(N-1\)\(2\times 1\)的方砖和\(2\)\(1\times 1\)的方砖填充,且两块\(1\times 1\)的方块不能有相邻的边,求合法方案数。

【分析】

啊,一道计数问题。反正我开始是这样想的。

如果没有那两块很碍事的砖,这不就是斐波拉契递推吗?,,,\(f[i]=f[i-1]+f[i-2]\),递推走起。

好,现在来看那两块碍事的砖。

首先,我们会发现,这两块特别的砖会把整个方格分成三个部分,我们假设左右两部分刚好是完整的(即是个矩形),那么中间的块就有性质了。

仔细推一推就会发现,当这两个特殊的块间隔奇数个块时,这两个块必定在相异的两行,并且中间只有一种方案构成。

同样的,当这两个特殊的块间隔偶数个块时,这两个块必定在相同的一行,并且中间也只有一种方案构成。

又因为不能有相邻的边,于是计算公式就出来了。

\[ans = 2 * \sum_{i=0}^{n-3}\sum_{j=0}^{i}f[j]*f[i-j] \]

细细理解下。

用这个大概只能得\(20pt\),我们想想怎么优化?看到\(N<=2e+9\)的数据范围,当然要往矩阵快速幂上面想咯。

矩阵的推法各有不同吧,我们来一步一步来拆这个式子。

\(g(i)=\sum_{j=0}^{i}f(j)*f(i-j)\)

所以

\(\begin{equation} \begin{aligned} g(i)&=\sum_{j=0}^{i}f(j)*f(i-j) \\ &=\sum_{j=0}^{i-2}f(j)*f(i-j)+f(i-1)*f(1)+f(i)*f(0)\\ &=\sum_{j=0}^{i-2}f(j)*[f(i-1-j)+f(i-2-j)]+f(i-1)+f(i)\\ &=g(i-2)+g(i-1)+f(i) \end{aligned} \end{equation}\)

又设\(sum(i)=\sum_{j=0}^{i}g(j)\)

所以

\(\begin{equation} \begin{aligned} sum(i)&=\sum_{j=0}^{i}g(j) \\ &=\sum_{j=0}^{i-1}g(j)+g(i) \\ &=sum(i-1)+g(i-2)+g(i-1)+f(i) \end{aligned} \end{equation}\)

所以易推得矩阵转移方程:

\(\begin{equation}{ \left[ \begin{array}{ccc} f(i) \\ f(i-1) \\ g(i) \\ g(i-1) \\ sum(i) \\ \end{array} \right ]}\times { \left[ \begin{array}{ccc} 1 & 1 & 0 & 0 & 0\\ 1 & 0 & 0 & 0 & 0 \\ 1 & 1 & 1 & 1 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 1 & 1 & 1 & 1 & 1 \\ \end{array} \right ]}={ \left[ \begin{array}{ccc} f(i+1)\\ f(i)\\ g(i+1)\\ g(i)\\ sum(i+1) \end{array} \right ]} \end{equation}\)

敲公式比敲字累多了。。。

于是\(O(125logn)\)可过。

哦,这个推法有点玄学,需要处理下边界情况,具体情况见代码。

【Code】

#include<ctime>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int MAX = 100000 + 5;
const int mod = 1e9 + 7;
inline int read(){
	int f = 1, x = 0;char ch;
	do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9');
	do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
	return f*x;
}

struct sakura {
	ll mar[5][5];
}A;

int t, n;

inline sakura mul(sakura A, sakura B) {
	sakura C;
	memset(C.mar, 0, sizeof (C.mar));
	for (int i = 0;i <= 4; ++i) {
	for (int k = 0;k <= 4; ++k) {		
		for (int j = 0;j <= 4; ++j) {
				C.mar[i][j] = (C.mar[i][j] + (A.mar[i][k] * B.mar[k][j]) % mod ) % mod;
			}
		}
	}
	return C;
}

inline sakura mi(sakura A, int c) {
	sakura B;
	B.mar[0][0] = 1, B.mar[1][0] = 1, B.mar[2][0] = 2, B.mar[3][0] = 1, B.mar[4][0] = 3;
    for (;c;c >>= 1) {
		if (c & 1) 	B = mul(A, B);
		A = mul(A, A);
	}
	return B;
}
int main(){
	t = read();
	while (t--) {
		n = read();
		if (n < 3) {
			printf("0\n");
			continue;
		}
		if (n == 3) {
			printf("2\n");
			continue;
		}
		A.mar[0][0] = 1, A.mar[0][1] = 1, A.mar[0][2] = 0, A.mar[0][3] = 0, A.mar[0][4] = 0;
		A.mar[1][0] = 1, A.mar[1][1] = 0, A.mar[1][2] = 0, A.mar[1][3] = 0, A.mar[1][4] = 0;
		A.mar[2][0] = 1, A.mar[2][1] = 1, A.mar[2][2] = 1, A.mar[2][3] = 1, A.mar[2][4] = 0;
		A.mar[3][0] = 0, A.mar[3][1] = 0, A.mar[3][2] = 1, A.mar[3][3] = 0, A.mar[3][4] = 0;
		A.mar[4][0] = 1, A.mar[4][1] = 1, A.mar[4][2] = 1, A.mar[4][3] = 1, A.mar[4][4] = 1;
		sakura ans = mi(A, n - 4);
		ans.mar[4][0] <<= 1;
		ans.mar[4][0] %= mod;
		printf("%d\n", ans.mar[4][0]);
	}
	return 0;
}
posted @ 2019-05-26 21:16  SilentEAG  阅读(252)  评论(0编辑  收藏  举报