- 要用(n)块(2 imes 1)的方砖去铺一条(2 imes n)的路。
- 其中有一块方砖裂成了两块(1 imes 1)的方砖,问有多少种铺路的方案使得裂成的两块方砖没有相邻边。
- 数据组数(le500,nle2 imes10^9)
(1 imes1)方砖两侧的填法
假设铺一条(2 imes i)的路的方案数为(f_i)。
考虑对于第一列第一个格子,放在它上面的方砖只有两种方式——竖着或是横着。
- 如果竖着,那么接下来相当于要铺一条(2 imes(i-1))的路,所以(f_i)可以由(f_{i-1})转移。
- 如果横着,考虑这样一来第一列第二个格子也必须横着,相当于把前两列都填完了,因此接下来要铺(2 imes(i-2))的路,所以(f_i)可以由(f_{i-2})转移。
综上所述,(f_i=f_{i-1}+f_{i-2}),也就是说它是斐波那契数!
加上(1 imes 1)方砖后的填法
考虑我们要铺一条(2 imes i)的路,强制(1 imes 1)的方砖分别在路的两侧。
如果(ile2),显然无解。
否则,发现当(i)为奇数时,两个方砖只能一个左上一个右下,或是一个左下一个右上,且中间的方砖只有唯一的填法;而当(i)为偶数时,两个方砖只能同时在上或同时在下,中间的方砖也只有唯一的填法。
所以说我们设加上(1 imes1)的方砖后铺一条(2 imes i)的路的方案数为(F_i)。
依旧考虑第一列的两个格子,那么首先第一种情况就是它可以不放(1 imes 1)的方砖,那么转移和之前类似,就是从(F_{i-1}+F_{i-2})转移。
还有一种情况就是它放了(1 imes 1)的方砖,它有两种摆放方式,因此可以从(2sum_{j=0}^{i-3}f_j)转移(注意是(f),因为只能填一次(1 imes1)的方砖)。
(sum_{j=0}^{i-3}f_j)可以直接维护,当然更好的方式是看出这就等于(f_{i-1}-1)(证明考虑归纳,首先(f_0=f_2-1),那么每次给(sum_{j=0}^{i-3}f_j=f_{i-1}-1)两侧同时加上(f_{i-2})即可得到(sum_{j=0}^{i-2}f_j=f_{i-2}+f_{i-1}-1=f_i-1))。
(n)这么大显然用矩阵加速,只要维护(F_i,F_{i-1},f_i,f_{i-1},2)五个值即可。
代码:(O(Tlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define X 1000000007
using namespace std;
int n;struct M
{
int a[5][5];I M() {memset(a,0,sizeof(a));}
I int* operator [] (CI x) {return a[x];}I Con int* operator [] (CI x) Con {return a[x];}
I M operator * (Con M& o) Con {M t;RI i,j,k;for(i=0;i^5;++i)//矩阵乘法
for(j=0;j^5;++j) for(k=0;k^5;++k) t[i][j]=(1LL*a[i][k]*o[k][j]+t[i][j])%X;return t;}
I M operator ^ (RI y) Con {M x=*this,t;for(RI i=0;i^5;++i) t[i][i]=1;W(y) y&1&&(t=t*x,0),x=x*x,y>>=1;return t;}//矩阵快速幂
}S,U,G;
int main()
{
S[2][2]=S[3][3]=1,S[4][4]=2,U[0][0]=U[0][1]=U[1][0]=U[2][2]=U[2][3]=U[3][2]=U[4][4]=1,U[2][0]=2,U[4][0]=X-1;//初始化初始矩阵和转移矩阵
RI Tt;scanf("%d",&Tt);W(Tt--) scanf("%d",&n),G=S*(U^(n-1)),printf("%d
",(0LL+G[2][0]+G[3][0]+G[4][0])%X);return 0;//每次直接矩阵快速幂
}