[BZOJ3328]PYXFIB/[JZOJ5150]卻不悔付此華年

題目大意

給定n,K,P ,求

i=0nk(niK)fibiK

P 取模的結果。
其中fibi 是斐波那契數列的第i 項。
一個測試點T 組數據。

其中1n1018,1k2×104,1p109,1T20
保證p1(modK)


題目分析

好一道腦洞題。
題目相當於求

i=0n(ni)fibi[i mod K=0]

把斐波那契數列寫成矩陣形式,令
A=(1110)

fibi=Ai11 ,題目變成求
i=0n(ni)Ai[i mod K=0]

先不考慮這個[i mod K=0] ,然後發現這個和二項式定理很像
i=0n(ni)xi=(x+1)n

因爲單位矩陣I 滿足可交換性,於是我們有
i=0n(ni)Ai=(A+I)n

現在考慮怎麼解決[i mod K=0] ,考慮構造一個函數F(x) 乘在Ai 後面,使其指數爲i 且可以等效代替[i mod K=0]
怎麼構造呢?考慮使用單位根的性質來構造。
g 爲模p 意義下的原根,設ω=gP1k 根據單位根的求和引理,我們有:
j=0K1ωij=0,K,i≢0(modK)i0(modK)

這個怎麼證明?你等比數列求和一下,把ωg 表示一下就好了。
於是答案等於
1Ki=0n(ni)Aij=0K1ωij

考慮把j 提出去
1Kj=0K1i=0n(ni)Aiωij=1Kj=0K1(ωijA+I)n

枚舉j ,然後後面的部分可以直接矩陣快速冪,然後就做完了ヽ(≧∀≦)ノ
原根暴力找(一般比較小),判斷的話只用檢查P1 除以P1 的質因子的次冪。
時間複雜度O(log2P+Klogn)

代碼實現

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long LL;

const int L=31622;

int pri[L+5],p[L+5],a[L+5];
bool mark[L+5];
int T,K,P,g,cnt;
LL n;

struct matrix
{
    int num[2][2];
    int r,c;

    inline matrix operator*(matrix const mat)const
    {
        matrix ret;ret.r=r,ret.c=mat.c,memset(ret.num,0,sizeof ret.num);
        for (int i=0;i<ret.r;++i)
            for (int k=0;k<c;++k)
                if (num[i][k])
                    for (int j=0;j<ret.c;++j)
                        (ret.num[i][j]+=1ll*num[i][k]*mat.num[k][j]%P)%=P;
        return ret;
    }

    inline matrix operator+(matrix const mat)const
    {
        matrix ret;ret.r=r,ret.c=c;
        for (int i=0;i<ret.r;++i)
            for (int j=0;j<ret.c;++j)
                ret.num[i][j]=(num[i][j]+mat.num[i][j])%P;
        return ret;
    }

    inline matrix operator*(int const k)const
    {
        matrix ret;ret.r=r,ret.c=c;
        for (int i=0;i<ret.r;++i)
            for (int j=0;j<ret.c;++j)
                ret.num[i][j]=1ll*num[i][j]*k%P;
        return ret;
    }
}A,I,res;

inline matrix operator*=(matrix &x,matrix y){return x=x*y;}
inline matrix operator+=(matrix &x,matrix y){return x=x+y;}
inline matrix operator*=(matrix &x,int y){return x=x*y;}

inline matrix operator^(matrix x,LL y)
{
    matrix ret=I;
    for (;y;y>>=1,x*=x) if (y&1) ret*=x;
    return ret;
}

int quick_power(int x,int y)
{
    int ret=1;
    for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
    return ret;
}

void pre()
{
    mark[1]=1;
    for (int i=2;i<=L;++i)
    {
        if (!mark[i]) pri[++pri[0]]=i;
        for (int j=1;j<=pri[0];++j)
        {
            if (1ll*i*pri[j]>L) break;
            mark[i*pri[j]]=1;
            if (!(i%pri[j])) break;
        }
    }
    A.r=A.c=2,A.num[0][0]=A.num[0][1]=A.num[1][0]=1;
    I.r=I.c=2,I.num[0][0]=I.num[1][1]=1;
}

void calc()
{
    cnt=0;
    int x=P-1;
    for (int i=1;i<=pri[0]&&1ll*pri[i]*pri[i]<=P-1&&x>1;++i)
        if (!(x%pri[i]))
        {
            p[++cnt]=pri[i],a[cnt]=0;
            for (;!(x%pri[i]);x/=pri[i]) ++a[cnt];
        }
    if (x>1) p[++cnt]=x,a[cnt]=1;
    g=0;
    for (int i=2;i<P&&!g;++i)
    {
        bool judge=1;
        for (int j=1;j<=cnt&&judge;++j) judge&=quick_power(i,(P-1)/p[j])!=1;
        if (judge) g=i;
    }
    int w=quick_power(g,(P-1)/K);
    res.r=res.c=2,memset(res.num,0,sizeof res.num);
    for (int j=0;j<K;++j) res+=((A*quick_power(w,j)+I)^n);
    res*=quick_power(K,P-2);
}

int main()
{
    freopen("you.in","r",stdin),freopen("you.out","w",stdout);
    for (pre(),scanf("%d",&T);T--;) scanf("%lld%d%d",&n,&K,&P),calc(),printf("%d\n",res.num[0][0]);
    fclose(stdin),fclose(stdout);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章