递推公式+矩阵乘方
转:(有个写的特别好,我就直接引用下了)
定义f[i][0]为走了i步恰好达到S的不同走法
定义f[i][1]为走了i步恰好达到A的不同走法
定义f[i][2]为走了i步恰好达到B的不同走法
定义f[i][3]为走了i步恰好达到C的不同走法
状态转义方程为:
f[i][0] = f[i – 1][1] + f[i – 1][2] + f[i – 1][3];
f[i][1] = f[i – 1][0] + f[i – 1][2] + f[i – 1][3];
f[i][2] = f[i – 1][0] + f[i – 1][1] + f[i – 1][3];
f[i][3] = f[i – 1][0] + f[i – 1][1] + f[i – 1][2];
由于n的规模达到109,所以我们可以令
矩阵A = [1 0 0 0]表示走了0步时恰好到达S, A, B, C的不同走法,
那么每次状态转义相当于乘以矩阵B,其中:
B = [0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0]
可知,求走n步恰好到达S点的不同走法即是求A * Bn,使用矩阵快速幂乘法即可。
快速幂:http://blog.csdn.net/masked__dance/article/details/40264411矩阵乘方,优化递推公式:http://blog.csdn.net/masked__dance/article/details/40351293
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define mod 1000000007
void mul(long long a[][4],long long b[][4])
{
long long c[4][4];
for(int i=0;i<4;i++)
for(int j=0;j<4;j++){
c[i][j]=0;
for(int k=0;k<4;k++)
c[i][j]+=((a[i][k]%mod)*(b[k][j]%mod))%mod;
}
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
b[i][j]=c[i][j]%mod;
}
long long quickPow(long long m,long long co[][4],long long a[][4])
{
while(m){
if(m&1)
mul(co,a);
mul(co,co);
m>>=1;
}
return a[0][0];
}
int main()
{
long long T,m;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&m);
long long
A[4][4]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
long long co[4][4]={0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,0};
cout<<quickPow(m,co,A)<<endl;
}
return 0;
}