Fib数模n的循环节 ZOJ Problem Set - 3729 Arnold

Fib数模n的循环节

对于一个正整数n,我们求Fib数模n的循环节的长度的方法如下:

(1)把n素因子分解,即

(2)分别计算Fib数模每个的循环节长度,假设长度分别是

(3)那么Fib模n的循环节长度

从上面三个步骤看来,貌似最困难的是第二步,那么我们如何求Fib模的循环节长度呢?

这里有一个优美的定理:Fib数模的最小循环节长度等于,其中表示Fib数模素数的最小循环节长度。可以看出我们现在最重要的就是求

对于求我们利用如下定理:

如果5是模的二次剩余,那么循环节的的长度是的因子,否则,循环节的长度是的因子。

顺便说一句,对于小于等于5的素数,我们直接特殊判断,loop(2)=3,loop(3)=8,loop(5)=20。

那么我们可以先求出所有的因子,然后用矩阵快速幂来一个一个判断,这样时间复杂度不会很大。
---------------------
作者:acdreamers
来源:CSDN
原文:https://blog.csdn.net/acdreamers/article/details/10983813
版权声明:本文为博主原创文章,转载请附上博文链接!

模板代码:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>

using namespace std;
typedef unsigned long long LL;

const int M = 2;

struct Matrix
{
    LL m[M][M];
};

Matrix A;
Matrix I = {1,0,0,1};

Matrix multi(Matrix a,Matrix b,LL MOD)
{
    Matrix c;
    for(int i=0; i<M; i++)
    {
        for(int j=0; j<M; j++)
        {
            c.m[i][j] = 0;
            for(int k=0; k<M; k++)
                c.m[i][j] = (c.m[i][j]%MOD + (a.m[i][k]%MOD)*(b.m[k][j]%MOD)%MOD)%MOD;
            c.m[i][j] %= MOD;
        }
    }
    return c;
}

Matrix power(Matrix a,LL k,LL MOD)
{
    Matrix ans = I,p = a;
    while(k)
    {
        if(k & 1)
        {
            ans = multi(ans,p,MOD);
            k--;
        }
        k >>= 1;
        p = multi(p,p,MOD);
    }
    return ans;
}

LL gcd(LL a,LL b)
{
    return b? gcd(b,a%b):a;
}

const int N = 400005;
const int NN = 5005;

LL num[NN],pri[NN];
LL fac[NN];
int cnt,c;

bool prime[N];
int p[N];
int k;

void isprime()
{
    k = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[k++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}

LL quick_mod(LL a,LL b,LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

LL legendre(LL a,LL p)
{
    if(quick_mod(a,(p-1)>>1,p)==1) return 1;
    else                           return -1;
}

void Solve(LL n,LL pri[],LL num[])
{
    cnt = 0;
    LL t = (LL)sqrt(1.0*n);
    for(int i=0; p[i]<=t; i++)
    {
        if(n%p[i]==0)
        {
            int a = 0;
            pri[cnt] = p[i];
            while(n%p[i]==0)
            {
                a++;
                n /= p[i];
            }
            num[cnt] = a;
            cnt++;
        }
    }
    if(n > 1)
    {
        pri[cnt] = n;
        num[cnt] = 1;
        cnt++;
    }
}

void Work(LL n)
{
    c = 0;
    LL t = (LL)sqrt(1.0*n);
    for(int i=1; i<=t; i++)
    {
        if(n % i == 0)
        {
            if(i * i == n) fac[c++] = i;
            else
            {
                fac[c++] = i;
                fac[c++] = n / i;
            }
        }
    }
}

LL find_loop(LL n)
{
    Solve(n,pri,num);
    LL ans=1;
    for(int i=0; i<cnt; i++)
    {
        LL record=1;
        if(pri[i]==2)
            record=3;
        else if(pri[i]==3)
            record=8;
        else if(pri[i]==5)
            record=20;
        else
        {
            if(legendre(5,pri[i])==1)
                Work(pri[i]-1);
            else
                Work(2*(pri[i]+1));
            sort(fac,fac+c);
            for(int k=0; k<c; k++)
            {
                Matrix a = power(A,fac[k]-1,pri[i]);
                LL x = (a.m[0][0]%pri[i]+a.m[0][1]%pri[i])%pri[i];
                LL y = (a.m[1][0]%pri[i]+a.m[1][1]%pri[i])%pri[i];
                if(x==1 && y==0)
                {
                    record = fac[k];
                    break;
                }
            }
        }
        for(int k=1; k<num[i]; k++)
            record *= pri[i];
        ans = ans/gcd(ans,record)*record;
    }
    return ans;
}

void Init()
{
    A.m[0][0] = 1;
    A.m[0][1] = 1;
    A.m[1][0] = 1;
    A.m[1][1] = 0;
}

int main()
{
    LL n;
    Init();
    isprime();
    while(cin>>n)
        cout<<find_loop(n)<<endl;
    return 0;
}

例题:Arnold  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3729

原文地址:https://www.cnblogs.com/geraldg/p/11254396.html

时间: 2024-08-09 22:56:42

Fib数模n的循环节 ZOJ Problem Set - 3729 Arnold的相关文章

广义Fibonacci数列模n的循环节

见这里:http://blog.csdn.net/ACdreamers/article/details/25616461 有详细的分析推理 只找出了循环节的上限,设 f[n] = (af[n - 1] + b[n - 2])%P,设序列a ={ f[1], f[2] }, 考虑t项后, b ={ f[t + 1], f[t + 2] }, 当t = p * p + 1时, 最多可以产生p * p + 1个不同的序列,其中至少会有一个与a相同, 而一旦与a相同,那么后面的项也会与a后面的项一样,所

uva 11582(大fib,打表找循环节)

f (0) = 0 and f (1) = 1 f (i+2) = f (i+1) + f (i)  for every i ≥ 0 Sample input three integers a,b,n where 0 ≤ a,b < 264 (a and b will not both be zero) and 1 ≤ n ≤ 1000. T a  b  n 3 1 1 2 2 3 1000 18446744073709551615 18446744073709551615 1000 Sampl

acdream 1060 递推数 (矩阵快速幂+循环节)

链接:click here~~ 题意: 递推数 Problem Description 已知A(0) = 0 , A(1) = 1 , A(n) = 3 * A(n-1) + A(n-2) (n ≥ 2)    求 A(A(A(A(N)))) Mod (1e9 + 7) Input 第一行一个整数 T (T ≤ 10000) 代表数据组数 每组数据占一行,一个整数 n (1 ≤ n ≤ 1e12) Output 对于每组测试数据输出一个整数. Sample Input 4 1 23574 278

ACdream - 1060 递推数(矩阵+循环节)

https://vjudge.net/problem/71677/origin 已知A(0) = 0 , A(1) = 1 , A(n) = 3 * A(n-1) + A(n-2) (n ≥ 2) 求 A(A(A(A(N)))) Mod (1e9 + 7) Input第一行一个整数 T (T ≤ 10000) 代表数据组数 每组数据占一行,一个整数 n (1 ≤ n ≤ 1e12) Output对于每组测试数据输出一个整数.Sample Input412357427870913Sample Ou

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): 1785    Accepted Submission(s): 651 Problem Description According to a r

ACM学习历程—51NOD 1770数数字(循环节)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1770 这是这次BSG白山极客挑战赛的A题.由于数字全部相同,乘上b必然会有循环节,于是模拟乘法,记录数据,出现循环就退出即可. 代码: #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring>

hdu-2814-Interesting Fibonacci-斐波那契循环节

哇塞,我竟然2A了....没有1A纯粹是脑残了.. 求:F(a^b)^(F(a^b) ^ (n-1))%c 既是求F(a^b)^(F(a^b) ^ (n-1)%phi[c]+phi[c])%c 先求x=F(a^b)%phi[c],有循环节,直接找到循环节就OK. 然后求y=F(a^b)%c,同求x,循环节. 然后问题就变成求y^(x^(n-1)%phi[c]+phi[c]) 直接套两次快速幂取模就OK. #include <iostream> #include<stdio.h> #

hdu1005-Number Sequence-(循环节)

题意:已知f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7,给出A,B,n,求f(n) 题解:n巨大,循环肯定超时,在模7的条件下,0<=f(n)<=6,一共7种选择,则f(n-1)和f(n-2)各有7种选择,共49种组合,至少在第50个组合必定会和前面的重复,找出循环节. 坑:不知网上为什么都说找连续两个1的循环节,有大神指出这是错误的,当a和b是某两个数时,序列是1,4,6循环,但我忘记是哪两个数了,也找不到博客.

斐波那契循环节

斐波那契循环节 从一道题引出一个算法:斐波那契数列 这道题并没有什么花里胡哨的条件,就是很简单的计算\(F(n)\ mod\ p\). 但是这题的\(n\)达到了\(10^{30000000}\)级别,很显然不能直接用矩阵快速幂做. 因此我们要引入一个概念:斐波那契循环节. 显而易见的是通过看题解我们知道,斐波那契数列在模一个数时会出现循环,而这个周期的长度就称为斐波那契循环节. 所以我们只需要求出斐波那契循环节\(m\),然后用矩阵快速幂计算\(F(n\ mod\ m)\ mod\ p\)就行