【题目大意】
在(2 imes N)的方格中用(N-1)块(2 imes 1)的方砖和(2)块(1 imes 1)的方砖填充,且两块(1 imes 1)的方块不能有相邻的边,求合法方案数。
【分析】
啊,一道计数问题。反正我开始是这样想的。
如果没有那两块很碍事的砖,这不就是斐波拉契递推吗?,,,(f[i]=f[i-1]+f[i-2]),递推走起。
好,现在来看那两块碍事的砖。
首先,我们会发现,这两块特别的砖会把整个方格分成三个部分,我们假设左右两部分刚好是完整的(即是个矩形),那么中间的块就有性质了。
仔细推一推就会发现,当这两个特殊的块间隔奇数个块时,这两个块必定在相异的两行,并且中间只有一种方案构成。
同样的,当这两个特殊的块间隔偶数个块时,这两个块必定在相同的一行,并且中间也只有一种方案构成。
又因为不能有相邻的边,于是计算公式就出来了。
细细理解下。
用这个大概只能得(20pt),我们想想怎么优化?看到(N<=2e+9)的数据范围,当然要往矩阵快速幂上面想咯。
矩阵的推法各有不同吧,我们来一步一步来拆这个式子。
设(g(i)=sum_{j=0}^{i}f(j)*f(i-j))。
所以
(egin{equation} egin{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))
所以
(egin{equation} egin{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})
所以易推得矩阵转移方程:
(egin{equation}{ left[ egin{array}{ccc} f(i) \ f(i-1) \ g(i) \ g(i-1) \ sum(i) \ end{array} ight ]} imes { left[ egin{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} ight ]}={ left[ egin{array}{ccc} f(i+1)\ f(i)\ g(i+1)\ g(i)\ sum(i+1) end{array} ight ]} 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
");
continue;
}
if (n == 3) {
printf("2
");
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
", ans.mar[4][0]);
}
return 0;
}