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

A Short problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2955 Accepted Submission(s): 1032

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 <= 1018), You should solve for

g(g(g(n))) mod 109 + 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

题意

给你n求 g ( g ( g ( n ) ) ) m o d 10 9 + 7 的值,其中 g ( x ) = 3 g ( x 1 ) + g ( x 2 )

思路

这个问题应该从最外层向里思考,对于 g ( x ) = 3 g ( x 1 ) + g ( x 2 ) 来说对 1 e 9 + 7 取余的话,根据取模和递推方程的特性当再次出现连着的两项是0和1的话那么接下去的项数就会之前的重复了,那我们可以这样考虑因为g(x)出现了循环节那么 g ( x % ) = g ( x ) 由于n的范围是1到1e18所以我们求循环节可以这样求

long long mod1=1e9+7;
long long MAX=1e18;
int main()
{
    long long a=0;
    long long b=1;
    for(long long i=2;i<MAX;i++)
    {
        long long temp=(3*b%mod+a%mod)%mod;
        a=b;
        b=temp;
        if(a==0&&b==1)
        {
            printf("%d\n",i-1);
            break;
        }
    }
    return 0;
}

找出的循环节为 222222224
我们知道了第一个 g ( x ) 中的x是以 222222224 为循环节的也就是里面的x对 222222224 有重复,原题套了3层那么第二层就是 g ( g ( x ) ) ,由于外层的g(x)中x也就是现在的g(x)对 222222224 有重复那么我们可以同样的找一下g(x)对222222224的重复得到为 183120

long long mod1=1e9+7;
long long mod2=222222224;
long long MAX=1e18;
int main()
{
    long long a=0;
    long long b=1;
    for(long long i=2;i<MAX;i++)
    {
        long long temp=(3*b%mod2+a%mod2)%mod2;
        a=b;
        b=temp;
        if(a==0&&b==1)
        {
            printf("%d\n",i-1);
            break;
        }
    }
    return 0;
}

同样的找一下最后一层的循环节得到240

long long mod1=1e9+7;
long long mod2=222222224;
long long mod3=183120;
long long MAX=1e18;
int main()
{
    long long a=0;
    long long b=1;
    for(long long i=2;i<MAX;i++)
    {
        long long temp=(3*b%mod3+a%mod3)%mod3;
        a=b;
        b=temp;
        if(a==0&&b==1)
        {
            printf("%d\n",i-1);
            break;
        }
    }
    return 0;
}

那么当他给我们n的时候我们可以从最里面一层一次对其取余得到最外层的x

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#define MAX 2
using namespace std;
long long mod1=1e9+7;
long long mod2=222222224;
long long mod3=183120;
typedef struct {
long long m[MAX][MAX];
}Matrix;
Matrix P={3,1,1,0};
Matrix I={1,0,0,1};
Matrix Matrixmul(Matrix a,Matrix b,long long mod)
{
    int i,j,k;
    Matrix c;
    for(i=0;i<MAX;i++)
        for(j=0;j<MAX;j++)
        {
            c.m[i][j]=0;
            for(k=0;k<MAX;k++)
            {
                c.m[i][j]+=(a.m[i][k]*b.m[k][j])%mod;
            }
            c.m[i][j]%=mod;
        }
        return c;
}
Matrix quickpow(long long n,long long mod)
{
    Matrix m=P,b=I;
    while(n>0)
    {
        if(n%2==1)
            b=Matrixmul(b,m,mod);
        n=n/2;
        m=Matrixmul(m,m,mod);
    }
    return b;
}
int main()
{
    long long n;
    while(scanf("%lld",&n)!=EOF)
    {
        if(n==0)
            {
                printf("0\n");
                continue;
            }
        n%=240;
        if(n==0)
            {
                printf("0\n");
                continue;
            }
        Matrix A=quickpow(n-1,mod3);
        n=A.m[0][0]%mod3;
        if(n==0)
        {
            printf("0\n");
            continue;
        }
        A=quickpow(n-1,mod2);
        n=A.m[0][0]%mod2;
        if(n==0)
        {
            printf("0\n");
            continue;
        }
        A=quickpow(n-1,mod1);
        printf("%lld\n",A.m[0][0]%mod1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/80209158