(转载)广义Fibonacci数列找循环节(心得+知识总结)

思路来源

https://blog.csdn.net/acdreamers/article/details/10983813(Fib数模n的循环节的长度,可参考51nod 1195)

https://blog.csdn.net/ACdreamers/article/details/25616461(广义Fibonacci数列找循环节)

知识整理

求Fib数模n的最小循环节的长度的方法:

1.把n素因子分解,即

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

 

Fib数模的最小循环节长度等于

其中表示Fib数模素数的最小循环节长度。

 

的方法论:(详细证明见第二篇博文)

,那么分情况讨论,注意p为奇素数,需特判p==2

是模的二次剩余时,枚举的因子

是模的二次非剩余时,枚举的因子

③找最小的因子,使得

 

成立。

3.Fib模n的循环节长度

 

求Fib数模n的循环节(非最小)的长度的方法:

注意到,上述方法中,无论何种情况,一定是(p+1)(p-1)的因子

那么,Fib数模p_{i}^{m}的循环节长度x_{i},一定是(p+1)(p-1)*p^{m-1}的因子,暴力的做法

ans=(x_{1},x_{2},...,x_{k})即为所求,也不求lcm了,循环节最多变为原数的完全平方倍

 

例题

2019牛客暑期多校训练营(第五场)B.generator 1

求广义Fibonacci数列的1e(1e6)项%mod的值,mod在(1e9,2e9]之间

法一:变二进制矩阵快速幂为十进制矩阵快速幂,每一位进行一次快速幂

法二:求循环节,将1e(1e6)模循环节化为ll可表示的幂次,再进行快速幂

代码1(十进制矩阵快速幂)

减少取模次数,可以卡过,也可以用2 4 8的倍增 用8+2 去凑10次 就不用写快速幂了

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 2;
const int N=1e6+10;
typedef long long ll;
int MOD;
char s[N];
ll x0,x1,a,b;
struct mat {
    ll c[MAXN][MAXN];
    int m, n;
    mat(){
    	memset(c, 0, sizeof(c));
    	m=n=MAXN;
    } 
    mat(int a, int b) : m(a), n(b) {
        memset(c, 0, sizeof(c));
    }
    void clear(){
		memset(c, 0, sizeof(c)); 
    }
    mat operator * (const mat& temp) {
        mat ans(m, temp.n);
        for (int i = 0; i < m; i ++)
            for (int j = 0; j < temp.n; j ++)
            {
                for (int k = 0; k < n; k ++)
                    ans.c[i][j] += c[i][k] * temp.c[k][j];
                ans.c[i][j]%=MOD;
            }
        return ans;
    }
    friend mat operator ^(mat M, int n) 
	{
   		 mat ans(M.m, M.m);
    	for (int i = 0; i < M.m; i ++)
        ans.c[i][i] = 1; 
   		 while (n > 0) {
        if (n & 1) ans = ans * M;
        M = M * M;
        n >>= 1;
    	}
    return ans;
	}
}bs,res;

int main()
{
	scanf("%lld%lld%lld%lld",&x0,&x1,&a,&b);
	scanf("%s%d",s,&MOD);
	bs.c[0][0]=a;bs.c[0][1]=b;
	bs.c[1][0]=1;
	res.c[0][0]=res.c[1][1]=1;
	int len=strlen(s);
	ll now=0;
	for(int i=len-1;i>=0;--i)
	{
		int v=s[i]-'0';
		res=res*(bs^v);//res*s[i]倍的基底 
		bs=bs^10;//基底扩大为原来10次方的基底 
	}
	printf("%lld\n",(res.c[1][0]*x1%MOD+res.c[1][1]*x0%MOD)%MOD); 
	return 0;
}

代码2(广义Fibonacci循环节)

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 45000
typedef __int128 LL;
const int M=1e6+10;
LL f0,f1,a,b;
LL N,P;
LL prime[maxn];
LL fac[maxn];
char s[M];
inline __int128 read()
{
   long long X=0,w=0; char ch=0;
   while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
   while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
   return w?-X:X;
}
inline void print(__int128 x)
{  
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}
void Prime(){
    memset(prime,0,sizeof(prime));
    for(int i=2;i<maxn;i++){
        if(!prime[i]) prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&prime[j]<maxn/i;j++){
            prime[prime[j]*i]=1;
            if(i%prime[j]==0){
                break;
            }
        }
    }
}
LL factor[130][2];
int fatcnt;
int get_factors(LL n){
    fatcnt=0;
    LL tmp=n;
    for(int i=1;prime[i]<=tmp/prime[i];i++){
        factor[fatcnt][1]=0;
        if(tmp%prime[i]==0){
            factor[fatcnt][0]=prime[i];
            while(tmp%prime[i]==0){
                tmp/=prime[i];
                factor[fatcnt][1]++;
            }
            fatcnt++;
        }
    }
    if(tmp!=1){
        factor[fatcnt][0]=tmp;
        factor[fatcnt][1]=1;
        fatcnt++;
    }
    return fatcnt;
}
LL gcd(LL a,LL b){
    if(b==0){
        return a;
    }
    else{
        return gcd(b,a%b);
    }
}
LL lcm(LL a,LL b){
    return a/gcd(a,b)*b;
}
struct Matrix{
    LL m[2][2];
}E,D;
Matrix Multi(Matrix A,Matrix B,LL mod){
    Matrix ans;
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            ans.m[i][j]=0;
            for(int k=0;k<2;k++){
                ans.m[i][j]+=(A.m[i][k]*B.m[k][j])%mod;
                if(ans.m[i][j]>=mod){
                    ans.m[i][j]-=mod;
                }
            }
        }
    }
    return ans;
}
void init(){
    memset(E.m,0,sizeof(E.m));
    memset(D.m,0,sizeof(D.m));
    D.m[0][0]=a;D.m[0][1]=b;
    D.m[1][0]=1;
    for(int i=0;i<2;i++){
        E.m[i][i]=1;
    }
    Prime();
}
Matrix Pow(Matrix A,LL e,LL mod){
    Matrix ans=E;
    while(e){
        if(e&1){
            ans=Multi(ans,A,mod);
        }
        A=Multi(A,A,mod);
        e>>=1;
    }
    return ans;
}
LL Pow(LL a,LL b,LL mod){
    LL ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%mod;
        }
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
LL get_fib(LL n,LL mod)
{
    if(mod==1) return 0;
    if(n==0)return f0%mod;
    Matrix ans=Pow(D,n-1,mod);
    return (ans.m[0][0]*f1%mod+ans.m[0][1]*f0%mod)%mod;
}
LL find_loop(LL n)
{
    get_factors(n);
    LL ans=1;
    for(int i=0;i<fatcnt;i++)
    {
        LL record=(factor[i][0]+1)*(factor[i][0]-1);
        for(int j=1;j<factor[i][1];j++)
        record*=factor[i][0];
        //printf("record:");print(record);puts("");
        if(record)ans=ans*record;
    }
    //printf("ans:");print(ans);puts("");
    return ans;
}
int main()
{
        f0=read();//print(f0);puts("");
        f1=read();//print(f1);puts("");
        a=read();//print(a);puts("");
        b=read();//print(b);puts("");
        init();
        scanf("%s",s);
        P=read();
        LL mod2=find_loop(P);
        //printf("mod2:");print(mod2);puts("");
        int len=strlen(s);
        for(int i=0;i<len;++i)
        N=(N*10+(s[i]-'0'))%mod2;
        N=get_fib(N,P);
        print(N);
        puts("");
        return 0;
}
/*
1315 521 20185 5452831
999 1000000008
 */

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值