1. 程式人生 > >[bzoj2432][矩陣乘法]兔農

[bzoj2432][矩陣乘法]兔農

Description

農夫棟棟近年收入不景氣,正在他發愁如何能多賺點錢時,他聽到隔壁的小朋友在討論兔子繁殖的問題。

問題是這樣的:第一個月初有一對剛出生的小兔子,經過兩個月長大後,這對兔子從第三個月開始,每個月初生一對小兔子。新出生的小兔子生長兩個月後又能每個月生出一對小兔子。問第n個月有多少隻兔子?

聰明的你可能已經發現,第n個月的兔子數正好是第n個Fibonacci(斐波那契)數。棟棟不懂什麼是Fibonacci數,但他也發現了規律:第i+2個月的兔子數等於第i個月的兔子數加上第i+1個月的兔子數。前幾個月的兔子數依次為:

1 1 2 3 5 8 13 21 34 …

棟棟發現越到後面兔子數增長的越快,期待養兔子一定能賺大錢,於是棟棟在第一個月初買了一對小兔子開始飼養。

每天,棟棟都要給兔子們餵食,兔子們吃食時非常特別,總是每k對兔子圍成一圈,最後剩下的不足k對的圍成一圈,由於兔子特別害怕孤獨,從第三個月開始,如果吃食時圍成某一個圈的只有一對兔子,這對兔子就會很快死掉。

我們假設死去的總是剛出生的兔子,那麼每個月的兔子數仍然是可以計算的。例如,當k=7時,前幾個月的兔子數依次為:

1 1 2 3 5 7 12 19 31 49 80 …

給定n,你能幫助棟棟計算第n個月他有多少對兔子麼?由於答案可能非常大,你只需要告訴棟棟第n個月的兔子對數除p的餘數即可。

Input

輸入一行,包含三個正整數n, k, p。

Output

輸出一行,包含一個整數,表示棟棟第n個月的兔子對數除p的餘數。

Sample Input

6 7 100

Sample Output

7

HINT

1<=N<=10^18

2<=K<=10^6

2<=P<=10^9

題解

這個真心可愛的一逼.. 對我這種懶得打表的人就是不和諧 手玩一下樣例膜K 1 1 2 3 5 0 5 5 3 0 3 3 6 2 0 2 2 4 6 3 2 5 0 5 5 3 0 …. 顯然有一個性質 分段之後每段開頭一定是兩個相同的數 藉著這個可以推出第二個性質 迴圈節不會超過K段(如果有迴圈節的話) 設上一段結尾的數是x 下一段的數可以寫為

1x,1x,2x,3x,5x,.... 發現是一個斐波那契數列 當我們減一個1使得最後一個數成0的時候,應該有 xt1(modK) 發現t就是x在模K意義下的逆元 預處理小於K的數的逆元在斐波那契數列中第一個出現的位置 這個時候引入一個結論 斐波那契數列在模K意義下一定是以0 1 1 開頭的迴圈數列 且迴圈長度不超過6*K 暴力預處理即可 剩下就是轉移了 行之間轉移用A矩陣 列之間轉移用B矩陣 找到迴圈節後直接C矩陣處理轉移 程式碼還挺好看的..

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define mod K
#define LL long long
using namespace std;
LL n,K,P;
struct matrix
{
    LL m[4][4];
    matrix(){memset(m,0,sizeof(m));}
    friend matrix operator *(matrix u,matrix v)
    {
        matrix ret;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                for(int k=1;k<=3;k++)
                    ret.m[i][k]=(ret.m[i][k]+u.m[i][j]*v.m[j][k])%P;
        return ret;
    }
}tp[1100000],ans,A,B,C;int len[1100000];
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(a==0)
    {
        x=0;y=1;
        return b;
    }
    else
    {
        LL tx,ty;
        LL d=exgcd(b%a,a,tx,ty);
        x=ty-(b/a)*tx;
        y=tx;
        return d;
    }
}
LL getinv(LL p)
{
    LL A=p,B=mod,x,y,K=1;
    LL d=exgcd(A,B,x,y);
    if(d!=1)return -1;
    x=(x*(K/d)%(B/d)+(B/d))%(B/d);
    return x;
}
matrix pow_mod(matrix u,LL b)
{
    matrix ret;
    for(int i=1;i<=3;i++)ret.m[i][i]=1;
    while(b)
    {
        if(b&1)ret=ret*u;
        u=u*u;b>>=1;
    }
    return ret;
}
int gg[1100000];
int vis[1100000];LL f[6100000],inv[1100000];
int main()
{
    scanf("%lld%lld%lld",&n,&K,&P);
    ans.m[1][2]=ans.m[1][3]=1;
    A.m[1][1]=A.m[1][2]=A.m[2][1]=A.m[3][3]=1;
    B.m[1][1]=B.m[2][2]=B.m[3][3]=1;B.m[3][1]=-1;
    f[1]=f[2]=1;
    for(int i=3;;i++)
    {
        f[i]=(f[i-1]+f[i-2])%K;
        if(!vis[f[i]])vis[f[i]]=i;
        if(f[i]==f[i-1]&&f[i]==1)break;
    }
    memset(inv,0,sizeof(inv));
    LL x=1;bool isring=false;//是否出現迴圈節 
    while(n)
    {
        if(!inv[x])inv[x]=getinv(x);
        if(inv[x]==-1){ans=ans*pow_mod(A,n);n=0;}
        else
        {
            if(!gg[x]||isring)//出現迴圈節或者之前沒有訪問過這個餘數
            {
                gg[x]=1;
                if(!vis[inv[x]])//fib中沒有這個數
                    ans=ans*pow_mod(A,n),n=0;
                else
                {
                    len[x]=vis[inv[x]];
                    if(n>=len[x])
                    {
                        n-=len[x];
                        tp[x]=pow_mod(A,len[x])*B;
                        ans=ans*tp[x];x=x*(LL)f[len[x]-1]%K;
                    }
                    else ans=ans*pow_mod(A,n),n=0;

                }
            }
            else//再次訪問 出現迴圈節 
            {
                C.m[1][1]=C.m[2][2]=C.m[3][3]=1;
                LL sum=len[x];C=C*tp[x];
                for(LL i=x*(LL)f[len[x]-1]%K;i!=x;i=i*(LL)f[len[i]-1]%K)sum+=len[i],C=C*tp[i];
                ans=ans*pow_mod(C,n/sum);
                isring=true;n=n%sum;
            }
        }
    }
    printf("%lld\n",(ans.m[1][1]%P+P)%P);
    return 0;
}