51nod 1195:斐波那契数列的循环节 (二次剩余+常系数线性递推)

传送门

题意:
求Fib数列在 modn 意义下的循环节。

题解:

首先,将 n 分解质因数,得 n=pk11pk22..pkcc
G(x) modx 下的循环节。
显然有:

G(n)=lcmi=1c(G(pkii))

至于求 pkii 的循环节具体请看论文:
https://wenku.baidu.com/view/7fc328eb4693daef5ef73d87.html

步骤为:
p5 ,则直接返回值(小于5不满足以下性质),否则:
1.

G(pk)=G(p)pk1

2.
F(p)=x12x+2(5p121(modp))(5p121(modp))

即是判断 5 是否为p的二次剩余系。

3.

G(p)|F(p)

有了第三点,可以枚举约数来判断循环,时间复杂度接近 O(n13)
又因为判断时间为 O(logn) ,所以总复杂度为 O(n13logn)

至于判断推荐常数较小的常系数齐次线性递推,矩乘也可以做。

#include
#include
#define MP make_pair
#define PB push_back
#define SD second
#define FT first 
typedef long long ll;
using namespace std;
int T,n;
const ll INF=0x3f3f3f3f3f3f3f3f;
tr1::unordered_mapS;
inline ll power(ll a,ll b,ll mod){
    int rs=1;a=a%mod;
    for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)rs=1ll*rs*a%mod;
    return rs;  
}
inline ll gcd(ll x,ll y){return y?(gcd(y,x%y)):x;}
typedef pair<int,int> pii;
namespace SP1{
    vectorfac;
    ll nowlen,M;
    inline void mul(ll *a,ll *b,ll mod){
        unsigned long long bd=a[0]*b[0],bc=a[0]*b[1],ad=a[1]*b[0],ac=a[1]*b[1];
        a[1]=(bc+ad+ac)%mod,a[0]=(bd+ac)%mod;
    }
    inline void power_p(ll *a,ll b,ll mod){
        ll c[2]={1,0};
        for(;b;b>>=1,mul(a,a,mod))
            if(b&1)mul(c,a,mod);
        a[0]=c[0];a[1]=c[1];
    }
    inline bool check(ll sum){
        ll b[2]={0,1};
        power_p(b,sum,M);
        return b[1]==0&&b[0]==1;
    } 
    inline void dfs(int pos,ll sum){
        if(pos==fac.size()){
            (sum!=1&&check(sum))?(nowlen=min(nowlen,sum)):0;
            return;
        }
        ll rs=1;
        for(int i=0;i<=fac[pos].SD;++i){
            dfs(pos+1,sum*rs);
            rs*=fac[pos].FT;
        }
    }
    inline ll getlen(ll x){
        if(x==2)return 3;
        if(x==3)return 8;
        if(x==5)return 20;
        if(S.find(x)!=S.end())return S[x];
        ll base=(power(5,(x-1)/2,x)==1)?(x-1):(2*x+2);
        fac.clear();nowlen=INF;M=x;
        for(int i=2;i*i<=base;++i){
            if(!(base%i)){
                pii t=MP(i,0);
                while(!(base%i))base/=i,++t.SD;
                fac.PB(t);
            }
        }
        if(base!=1)fac.PB(MP(base,1));
        dfs(0,1);return S[x]=nowlen;
    }
}
vectorfac;
ll ans;
inline void solve(int n){
    if(n==1){puts("1");return;}
    fac.clear();
    for(int i=2;i*i<=n;i++){
        if(!(n%i)){
            pii t=MP(i,0);
            while(!(n%i))n/=i,++t.SD;
            fac.PB(t);
        }
    }
    if(n!=1)fac.PB(MP(n,1)); 
    ans=1;
    for(int i=0;i1,INF);
        ans=(ans*l)/gcd(ans,l);
    }
    printf("%llu\n",ans);
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        solve(n);
    }
}

你可能感兴趣的:(二次剩余,常系数齐次线性递推)