HDU - 4291 A Short problem (矩阵快速幂+找循环节)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4291

A Short problem

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2845    Accepted Submission(s): 995


Problem Description
  According to a research, VIM users tend to have shorter fingers, compared with Emacs users.
  Hence they prefer problems short, too. Here is a short one:
  Given n (1 <= n <= 10 18), You should solve for 
g(g(g(n))) mod 10 9 + 7

  where
g(n) = 3g(n - 1) + g(n - 2)

g(1) = 1

g(0) = 0

 

Input
  There are several test cases. For each test case there is an integer n in a single line.
  Please process until EOF (End Of File).
 

Output
  For each test case, please print a single line with a integer, the corresponding answer to this case.
 

Sample Input
  
  
0 1 2
 

Sample Output
  
  
0 1 42837
 

Source
题解: 当看到公式的时候就知道要用矩阵快速幂,这就是它的功能。然而出现了个连环套的函数,一开始就将它分解了
内层函数看做外层函数的自变量,一开始是这么想的,在求快速幂的时候,自变量就是矩阵的指数,但是由内层
函数得出的数非常大,假设为k,那么g(k)就是求矩阵的(k-1)次方了然后模MOD,
因为有欧拉定理或者费马小定理知道对于一个数a^k%p=a^(k%oula(p))%p,然后我对矩阵快速幂也这么用了。但是wa,
从感性上能觉出这样取模的话会不精确容易出现票偏差,但是不知道怎么证明,,囧!
正确的方法是找每个mod的循环节,这样是没有任何偏差的了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;
const LL mod1=1000000007;//求结果的循环节
const LL mod2=222222224;//第1层的循环节,假设g(g(g(n)))=g(x),即mod2是x的循环节
const LL mod3=183120;//第2层的循环节假设g(g(g(n)))=g(g(y)),即mod3是y的循化节
LL MOD;
struct mat{
    LL a[2][2];
    mat(){
        memset(a,0,sizeof(a));
    }
}e,ans;
mat  mul(mat p1,mat p2)
{
    mat t;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                t.a[i][k]+=p1.a[i][j]*p2.a[j][k];
                t.a[i][k]%=MOD;
            }
        }
    }
    return t;
}
mat mi(mat p,LL k)
{
    mat t;
    for(int i=0;i<2;i++)t.a[i][i]=1;
    while(k)
    {
        if(k&1)t=mul(t,p);
        k=k>>1;
        p=mul(p,p);
    }
    return t;
}
int main()
{
    /*long long x3, x1=0,x2=1;
     long long temp=1e9+7;
     for(long long i=2;;i++)
     {
         x3=(3*x2+x1)%temp;
         if(x3==1&&x2==0)
         {
             printf("%I64d\n",i-1);
             break;
         }
         x1=x2;
         x2=x3;
     }*/
    LL n;
    while(~scanf("%lld",&n))
    {
        if(n==0||n==1)
        {
            printf("%lld\n",n);
            continue;
        }
        e.a[0][0]=3;
        e.a[0][1]=1;
        e.a[1][0]=1;
        e.a[1][1]=0;
        MOD=mod3;
        ans=mi(e,n-1);
        MOD=mod2;
        if(ans.a[0][0]>=2)ans=mi(e,ans.a[0][0]-1);
        MOD=mod1;
        if(ans.a[0][0]>=2)ans=mi(e,ans.a[0][0]-1);
        printf("%lld\n",ans.a[0][0]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值