hrbust 1375 The Active Leyni【暴力打表+递推+矩阵快速幂】


The Active Leyni

Time Limit: 1000 MS

Memory Limit: 65536 K

 

Total Submit: 111(43 users)

Total Accepted: 51(40 users)

Rating: 

Special Judge: No

 

Description

There is a map of the town where Leyni lives.

The vertex S indicates the home of Leyni and the vertexes A, B, C indicate the homes of his friends. They are connected to each other as the map presents.

Leyni is too active that he can’t stay idle. It will count one step every time when he walks from one place to another. He starts from his home S and won’t stop.

He wonders the number of ways in which he can go from his home S to itself in exactly n steps. The number may be quite large, you should output it modulo 1000000007.

 

Input

There are multiple test cases. The first line of input is an integer T indicating the number of test cases. Then T test cases follow.

For each test case:

Line 1. This line contains an integer n (1 ≤ n ≤ 109) indicating the required steps.

Output

For each test case:

Line 1. Output the number of ways modulo 1000000007.

Sample Input

2

2

4

Sample Output

3

21

Hint

In the first sample, the possible paths are:

S=>A=>S

S=>B=>S

S=>C=>S

Source

哈理工2012春季校赛 - 网络预选赛

Author

齐达拉图@HRBUST

 

思路:

首先写一发暴力Dfs的代码,然后跑出大约十组左右的数据。

暴力代码:

#include<stdio.h>
#include<string.h>
using namespace std;
int cont;
void Dfs(int now,int cur,int zhong,char a[])
{
    if(cur==zhong)
    {
        if(a[zhong-1]=='S')
        {
            for(int i=0;i<zhong;i++)
            {
                printf("%c",a[i]);
            }
            printf("\n");
            cont++;
        }
        return ;
    }
    for(int i=0;i<=3;i++)
    {
        if(i==now)continue;
        if(i==0)a[cur]='S';
        if(i==1)a[cur]='A';
        if(i==2)a[cur]='B';
        if(i==3)a[cur]='C';
        Dfs(i,cur+1,zhong,a);
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        cont=0;
        char jilu[500];
        Dfs(0,0,n,jilu);
        printf("%d\n",cont);
    }
}
数据如下:

1 0

2 3

3 6

4 21

5 60

6 183

7 546

8 1641

9 4920

10 14763

然后默默的找规律啊找规律,找啊找啊找规律。递推出这样的结果:

6=3*3-3

21=6*3+3

60=21*3-3

183=60*3+3

....................

......................

.........................

然后我们就能得到这样一个递推式:Fn=Fn-1+Qn-1,Qn=-Qn-1

因为n比较大,所以我们这里还需要进行矩阵快速幂优化算法。

如果我们设第一个f1=3,那么Q1=-3,辣么也不难推出:


这里还要注意一个点,因为我们数据里边有负数,而且数据求余的时候可能导致有一个数求余之后减一个数的时候出现不该出现的负数,那么我们在矩阵乘法的时候,我们需要对这个求余之后的这个数先加上mod再进行运算。

这里举个栗子:

假如在矩阵乘法的过程中遇到了这样一种情况:

c+=(a*b)%mod,如果a或者是b有一个负数的话:

那么求得的解c是负数明显是不对的,其实这个负数是一个对mod求余之后的一个小数减去一个数得来的,也就是说假如a=-1,其实a的来源可能是这样:a=1000%999-2。所以我们在计算的时候,c+=(a+mod)*(b+mod);再对mod求余即可。这次我们得到的c就不会是负数啦~

AC代码:


#include<stdio.h>
#include<string.h>
using namespace std;
#define ll long long int
#define mod 1000000007
typedef struct Matrix
{
    ll mat[2][2];
}matrix;
matrix A,B,tmp;
Matrix matrix_mul(matrix a,matrix b)
{
    matrix c;
    memset(c.mat,0,sizeof(c.mat));
    int i,j,k;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                c.mat[i][j]+=(a.mat[i][k]+mod)*(b.mat[k][j]+mod);//细节处理。
                c.mat[i][j]%=mod;
            }
        }
    }
    return c;
}
Matrix matrix_quick_power(matrix a,ll k)//矩阵快速幂0.0
{
    matrix b;
    memset(b.mat,0,sizeof(b.mat));
    for(int i=0;i<2;i++)
    b.mat[i][i]=1;//单位矩阵b
    while(k)
    {
        if(k%2==1)
        {
            b=matrix_mul(a,b);
            k-=1;
        }
        else
        {
            a=matrix_mul(a,a);
            k/=2;
        }
    }
    return b;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n;
        scanf("%lld",&n);
        A.mat[0][0]=3;A.mat[0][1]=1;//我们通过推论得到的矩阵A
        A.mat[1][0]=0;A.mat[1][1]=-1;
        tmp.mat[0][0]=3;tmp.mat[0][1]=0;
        tmp.mat[1][0]=-3;tmp.mat[1][1]=0;
        if(n==1)
        {
            printf("0\n");
            continue;
        }
        else
        {
             B=matrix_quick_power(A,n-2);
             B=matrix_mul(B,tmp);
             printf("%lld\n",B.mat[0][0]);
        }
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值