小L的问题 | ||||||
| ||||||
Description | ||||||
小L是一个可爱的女孩,她特别喜欢玩多米诺骨牌。有一天她得到了一个宽度为2长度为n的棋盘。她现在有1*2大小的矩形骨牌和3格大小的L型骨牌(L型骨牌的样式参考Hint)。她发现有很多种方式可以让这个棋盘完全覆盖,她想知道对于每一个2*n大小的棋盘,用这两种骨牌完全覆盖的方案数。这个数字可能很大,所以答案需要模1000000007. | ||||||
Input | ||||||
输入数据第一行为T,代表数据组数。 接下来的T行中,每一行只有一个数字n,n为不超过1e18的正整数,代表棋盘的长度。 | ||||||
Output | ||||||
输出T行,每行为对应n的答案。 | ||||||
Sample Input | ||||||
1 2 | ||||||
Sample Output | ||||||
2 | ||||||
Hint | ||||||
L型骨牌如上图所示。 | ||||||
Source | ||||||
"科林明伦杯"哈尔滨理工大学第八届程序设计竞赛 |
思路:1e18的范围考虑矩阵快速幂,显然有dp[i] = dp[i-1]+dp[i-2]+2*(dp[i-3]+dp[i-4]+...+dp[0])。
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
struct Mat{ll mat[3][3];};
Mat operator*(Mat a, Mat b)
{
Mat c;
for(int i=0; i<3; ++i)
{
for(int j=0; j<3; ++j)
{
c.mat[i][j] = 0;
for(int k=0; k<3; ++k)
{
c.mat[i][j] += (a.mat[i][k]*b.mat[k][j])%mod;
c.mat[i][j] %= mod;
}
}
}
return c;
}
Mat operator ^(Mat a, ll b)
{
Mat c;
for(int i=0; i<3; ++i)
for(int j=0; j<3; ++j) c.mat[i][j]=(i==j);
for(;b;b>>=1)
{
if(b&1) c=c*a;
a=a*a;
}
return c;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll n;
scanf("%lld",&n);
if(n==1)
{
puts("1");
continue;
}
if(n==2)
{
puts("2");
continue;
}
Mat a, b;
for(int i=0; i<3; ++i)
for(int j=0; j<3; ++j) b.mat[i][j]=a.mat[i][j]=0;
a.mat[0][0]=2;
a.mat[0][1]=a.mat[0][2]=1;
b.mat[0][0] = b.mat[0][1]=b.mat[1][2]=b.mat[2][2]=b.mat[1][0]=1;
b.mat[2][0]=2;
b = b^(n-2);
a=a*b;
printf("%lld\n",a.mat[0][0]);
}
return 0;
}