《入门经典》——9.2

  Uva11582:

  基于斐波那契数列,给出a,b,mod,让你求解f(a^b) % mod的结果。

  分析:这是一道很看基本功的简单数论题目。

数据类型:题目给出的a、b的范围最大可以取到2^64,因此这里凡是用到a、b的运算的地方都要用ULL(unsigned long long),经过调试显示long long这个数据类型都不够。

  算法分析:本身斐波那契数列在一定项之后就变得异常大,结合同余运算的性质,因此这里我们考虑直接基于余数进行迭代求余。 而由于a、b的取值范围,我们显然不能打表打到a^b,因此我们推测取余结果必然存在周期性。

  进一步更详细的分析周期性存在的合理性:令F(i) = f(i) % mod , 这里f(i)是斐波那契数列。我们得到F(n)的线性序列,我们从二元组<F(i),F(i+1)>来看,在根据递推公式往后生成的时候,一旦<F(i),F(i+1)>曾经出现过,这就表明余数的线性序列的周期是i-1。而二元组最多有mod x mod个,因此F(i)的序列长度超过mod x mod之后,必然会出现周期性。

  总结起来,在这里处理数据巨大的技巧,归结为下面几个方面:

  1. 将f(i)映射到F(i) = f(i) % mod.
  2. 在证明了F(i)分布的周期性必然存在之后,寻找F(i)分布的周期性。
  3. 利用快速幂取模找到a^b处于一个周期中那个位置,即计算a^b % M.其中M是F(i)分布的周期M.

  简单的参考代码如下:

 #include<cstdio>

#include<vector>

#include<algorithm>

#include<iostream>

using namespace std;

const int maxn = 1001;

vector<int>f[maxn];

typedef unsigned long long ULL;

void init()

{

     for(int i = 2;i <= 1000;i++)

     {

          int mod = i;

           int a = 0 , b =  1 , c = (a+b)% mod;

           f[i].push_back(a);

           f[i].push_back(b);

           f[i].push_back(c);

 

          while(!(b == 0 && c == 1))

           {

               a = b;

               b = c;

               c = (a % mod +b % mod) % mod;

               f[i].push_back(c);

           }

           f[i].pop_back();

           f[i].pop_back();

     }

}

 

 ULL quick_mod(ULL a,ULL b, int _m)

 {

    ULL ans = 1;

    while(b){

        if(b&1){

            ans = (ans%_m) * (a%_m) % _m;

            b--;

        }

        b /= 2;

        a = (a%_m)*(a%_m)%_m;

    }

    return ans;

}

 

ULL n , m ;

int main()

{

    int t;

 

    int  Mod;

    init();

    scanf("%d",&t);

    while(t--)

    {

       cin >> n >> m >> Mod;

        if(Mod == 1)  {printf("0
");continue;}

        int _m = f[Mod].size();

        ULL temp = quick_mod(n , m , _m);

        printf("%d
",f[Mod][temp]);

    }

}
原文地址:https://www.cnblogs.com/rhythmic/p/5833111.html