HDU 4291解题报告

A Short problem

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


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
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   4296  4295  4294  4293  4292 

       这道题是比较裸的矩阵快速幂的题。但是与一般的题不一样的就是取模的是复合函数,而且是对最外层的取模,因此内部的数字可能会非常大。g(n)=3*g(n-1)+g(n-2)这个是标准的二阶线性递推式,我们可以类比斐波那契数列的公式。令Y=g(g(x)),mod=1000000007,则所求的转换成了g(Y)%mod的结果。这里出现了取模的符号。而本题的特点就在于数字会非常大。我们也知道二阶线性递推式取模必然会出现周期性的循环。因此我们可以暴力模拟得出g(Y)%mod的Y的周期,得到Y的周期为t1=222222224,即g(g(g(x)))%mod=g(g(g(x))-k*t1)%mod,所以计算时为了缩小范围我们只需计算g(g(x))%t1的值即可。即g(g(g(x)))%mod=g(g(g(x))%t1)%mod。要计算g(g(x))%t1,这样我们再次令Z=g(x),则此时所求结果为g(Z)%t1,这是可以得到Z的周期为t2=183120,即g(g(x))%t1=g(g(x)%t2)%t1。这样所求为g(x)%t2,我们再次暴力模拟,得到x的周期为t3=240。所以最终所求结果为g(g(g(x)))%mod=g(g(g(x%t3)%t2)%t1)%mod。这样通过层层取模我们便可以减小所得的数字的规模,防止数字过大导致的溢出问题。

       通过这道题的训练,我们发现该题的难点不在矩阵快速幂。因为我们很清楚的知道,当出现线性递推式时,而且在n特别大时就应该有矩阵快速幂降低复杂度的意识。而本题的复合函数是一个难点。而突破这个难点就要从取模周期上降低数字的规模。取模的周期性这些小的技巧在这种难题中我们一定要注意。往往容易被忽视的小技巧才是解决问题的关键之处。

       参考代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<iomanip>
#include<utility>
#define pb push_back
#define mp make_pair
#define CLR(x) memset(x,0,sizeof(x))
#define _CLR(x) memset(x,-1,sizeof(x))
#define REP(i,n) for(int i=0;i<n;i++)
#define Debug(x) cout<<#x<<"="<<x<<" "<<endl
#define REP(i,l,r) for(int i=l;i<=r;i++)
#define rep(i,l,r) for(int i=l;i<r;i++)
#define RREP(i,l,r) for(int i=l;i>=r;i--)
#define rrep(i,l,r) for(int i=1;i>r;i--)
#define read(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<11
using namespace std;

int T1=222222224,T2=183120,T3=240,mod=1000000007;
ll n;
struct mat
{
    ll d[2][2];
}A,B,E;

mat multi(mat a,mat b,int mod)
{
    mat ans;
    rep(i,0,2)
    {
        rep(j,0,2)
        {
            ans.d[i][j]=0;
            rep(k,0,2)
            {
                if(a.d[i][k]&&b.d[k][j])
                    ans.d[i][j]=(ans.d[i][j]+a.d[i][k]*b.d[k][j])%mod;
            }
        }
    }
    return ans;
}

mat quickmulti(mat a,ll n,int mod)
{
    mat ans=E;
    while(n)
    {
        if(n&1)
        {
            n--;
            ans=multi(ans,a,mod);
        }
        else
        {
            n>>=1;
            a=multi(a,a,mod);
        }
    }
    return ans;
}

int main()
{
   A.d[0][0]=3,A.d[0][1]=1,A.d[1][0]=1,A.d[1][1]=0;
   B.d[0][0]=1,B.d[0][1]=0,B.d[1][0]=0,B.d[1][1]=0;
   E.d[0][0]=1,E.d[0][1]=0,E.d[1][0]=0,E.d[1][1]=1;
   while(~scanf("%I64d",&n))
   {
       n%=T3;
       mat ans=quickmulti(A,n,T2);
       ans=multi(ans,B,T2);
       int shu=ans.d[1][0];
       ans=quickmulti(A,shu,T1);
       ans=multi(ans,B,T1);
       shu=ans.d[1][0];
       ans=quickmulti(A,shu,mod);
       ans=multi(ans,B,mod);
       printf("%I64d\n",ans.d[1][0]);
   }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值