【题目】
LOJ
有一条
2
×
n
2\times n
2×n的路,要用
n
−
1
n-1
n−1个
1
×
2
1\times 2
1×2的相同砖块和两个
1
×
1
1\times 1
1×1的相同砖块来铺路,要求两个小砖块没有邻边,求方案数。有
T
T
T组数据。
n
≤
2
×
1
0
9
,
T
≤
500
n\leq 2\times 10^9,T\leq 500
n≤2×109,T≤500
【解题思路】
首先如果没有那两个
1
×
1
1\times 1
1×1的砖块,答案就是
f
i
b
fib
fib,也就是考虑前两列的放置方式。不妨将
f
i
b
fib
fib记为
f
i
f_i
fi。
现在考虑两个 1 × 1 1\times 1 1×1,那么对于它们中间的方块,放置方式是唯一的。设答案为 g i g_i gi,实际上我们就是要考虑第二块 1 × 1 1\times 1 1×1放在哪里,它是一个 f f f的前缀和形式。不妨将 s s s记为 f f f的前缀和,那么我们有:
g
i
=
g
i
−
1
+
g
i
−
2
+
2
⋅
s
i
−
3
g_i=g_{i-1}+g_{i-2}+2\cdot s_{i-3}
gi=gi−1+gi−2+2⋅si−3
又根据
f
i
b
fib
fib的特性,我们可以得到
s
i
−
3
=
f
i
−
1
−
1
s_{i-3}=f_{i-1}-1
si−3=fi−1−1,那么柿子就可以写成:
g
i
=
g
i
−
1
+
g
i
−
2
+
2
⋅
f
i
−
1
−
2
g_{i}=g_{i-1}+g_{i-2}+2\cdot f_{i-1}-2
gi=gi−1+gi−2+2⋅fi−1−2
这个就可以写成矩阵的形式了,是一个
5
×
5
5\times 5
5×5的矩阵(还有常数项)。
初始 f 0 = f 1 = 1 , g 0 = g 1 = 0 f_0=f_1=1,g_0=g_1=0 f0=f1=1,g0=g1=0
复杂度 O ( T ⋅ 5 3 ⋅ log n ) O(T\cdot 5^3\cdot \log n) O(T⋅53⋅logn)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int mul(int x,int y){return 1ll*x*y%mod;}
struct Matrix
{
int a[5][5];
Matrix(){memset(a,0,sizeof(a));}
void init(){memset(a,0,sizeof(a));}
void one(){for(int i=0;i<5;++i)a[i][i]=1;}
Matrix operator *(const Matrix&A)const
{
Matrix res;
for(int i=0;i<5;++i) for(int j=0;j<5;++j) for(int k=0;k<5;++k)
up(res.a[i][j],mul(a[i][k],A.a[k][j]));
return res;
}
}bas,beg,now;
Matrix mpow(Matrix x,int y)
{
Matrix res;res.one();
for(;y;y>>=1,x=x*x)if(y&1)res=res*x;
return res;
}
int main()
{
#ifdef Durant_Lee
freopen("LOJ3086.in","r",stdin);
freopen("LOJ3086.out","w",stdout);
#endif
bas.a[0][0]=bas.a[0][1]=bas.a[1][0]=bas.a[2][2]=bas.a[2][3]=bas.a[3][2]=bas.a[4][4]=1;
bas.a[0][2]=2;bas.a[4][2]=mod-1;
beg.a[0][0]=beg.a[0][1]=1;beg.a[0][4]=2;
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
now=bas;now=beg*mpow(now,n-1);
printf("%d\n",now.a[0][2]);
}
return 0;
}