ACM学习历程—HDU 5451 Best Solver(Fibonacci数列 && 快速幂)(2015沈阳网赛1002题)

Problem Description
The so-called best problem solver can easily solve this problem, with his/her childhood sweetheart.

It is known that y=(5+2√6)^(1+2^x).
For a given integer x (0x<2^32) and a given prime number M (M46337) , print [y]%M . ([y] means the integer part of y )
 
Input
An integer T (1<T1000) , indicating there are T test cases.
Following are T lines, each containing two integers x and M , as introduced above.
 
Output
The output contains exactly T lines.
Each line contains an integer representing [y]%M .
 
Sample Input
7
0 46337
1 46337
3 46337
1 46337
21 46337
321 46337
4321 46337
 
Sample Output
Case #1: 97
Case #2: 969
Case #3: 16537
Case #4: 969
Case #5: 40453
Case #6: 10211
Case #7: 17947

题目大意就是求那个式子y=(5+2√6)^(1+2^x)。

考虑(5+2√6)^n和(5-2√6)^n;

分别设为A和B,

自然A*B=1

考虑A+B,发现奇数次幂的根号消掉了,于是是个整数。

由因为A>1,自然B<1所以说A的小数部分就是1-B,所以A的整数部分就是A+B-1。

于是就是求A+B,便能得到结果。

而这个式子跟特征根求解的一次线性递推式的结果很像。

于是考虑x^2+bx+c=0这个特征方程,跟为5+2√6和5-2√6。

得b=-10,c=1。

于是递推式为f(n+2)=10f(n+1)-f(n)。

然后本地打表发现,不管模范围内的任何素数,这个序列的循环节都不是很大。

于是考虑直接暴力循环节,不过记忆化下来。

然后就是考虑2^x模循环节的结果即可,这个用快速幂。

复杂度是O(r+logx),其中r为循环节大小。

题解中通过结论 (p^2- p)(p^2-1)为循环节使用矩阵快速幂.

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <string>
#define LL long long

using namespace std;

const int maxM = 47000;
int x, m, r[maxM], f[maxM];

void init()
{
    if (r[m] != -1)
        return;
    f[0] = 2%m;
    f[1] = 10%m;
    for (int i = 2;; ++i)
    {
        f[i] = ((10*f[i-1]-f[i-2])%m+m)%m;
        if (f[i-1] == f[0] && f[i] == f[1])
        {
            r[m] = i-1;
            break;
        }
    }
}

//快速幂m^n
int quickPow(LL x, int n, int mm)
{
    int a = 1;
    while (n)
    {
        a *= n&1 ? x : 1;
        a %= mm;
        n >>= 1 ;
        x *= x;
        x %= mm;
    }
    return a;
}

void work()
{
    int k, ans;
    k = quickPow(2, x, r[m]);
    k = (k+1)%r[m];
    f[0] = 2%m;
    f[1] = 10%m;
    for (int i = 2; i <= k; ++i)
        f[i] = ((10*f[i-1]-f[i-2])%m+m)%m;
    ans = (f[k]-1+m)%m;
    printf("%d
", ans);
}

int main()
{
    //freopen("test.in", "r", stdin);
    memset(r, -1, sizeof(r));
    int T;
    scanf("%d", &T);
    for (int times = 0; times < T; ++times)
    {
        printf("Case #%d: ", times+1);
        scanf("%d%d", &x, &m);
        init();
        work();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/andyqsmart/p/4822086.html