bzoj2432: [Noi2011]兔农 快速幂+数论

14 篇文章 0 订阅

不难发现,这个题就是求斐波那契数列改化,由于有一个很强的结论,斐波那契数列取模是一个周期数列,所以我们可以去找循环节,然后找到循环节后把这第一个循环节处理出来。 其实vfk说的很详细了,注意这里mod的数不一定是个质数,我们只能用拓展欧几里得求逆元。。。。

http://vfleaking.blog.163.com/blog/static/174807634201341721051604/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
#define oo 2000000000000000000000LL
#define ll long long
#define maxn 1000100
ll n,len[maxn],next[maxn];
int inv[maxn],k,mod;
void init()
{
    inv[1]=1;
    for(int i=2;i<k;i++)
    {
        inv[i]=(k-k/i)*inv[i]%k;
    }
}
ll gcd(ll &p,ll &q,ll x,ll y){
    if(x==0){
        p=1,q=1;
        return y;
    }
    ll r=gcd(q,p,y%x,x);
    p-=q*(y/x);
    return r;
}
ll gcd(ll x,ll y){
    return x==0?y:gcd(y%x,x);
}
ll ni(ll x)
{
    ll p,q;
    if(gcd(x,k)!=1)return 0;
    gcd(p,q,x,k);
    return (p%k+k)%k;
}
int pos[maxn],fib[maxn*6];
void firstap()
{
    memset(pos,-1,sizeof(pos));
    fib[0]=1;fib[1]=1;fib[2]=2;
    for(int i=3;!( fib[i-1]==1&& fib[i-2]==1);i++)
    {
        fib[i]=fib[i-1]+fib[i-2];
        if(fib[i]>=k) fib[i]-=k;
        if(pos[fib[i]]==-1) pos[fib[i]]=i;
    }
}
struct matrix
{
    int a[3][3];
    void cl()
    {
        memset(a,0,sizeof(a));
    }
    void one()
    {
        memset(a,0,sizeof(a));
        for(int i=0;i<3;i++) a[i][i]=1;
    }
    void A()
    {
        a[0][0]=1;a[0][1]=1;a[0][2]=0;
        a[1][0]=1;a[1][1]=0;a[1][2]=0;
        a[2][0]=0;a[2][1]=0;a[2][2]=1;
    }
    void B()
    {
        a[0][0]=1;a[0][1]=1;a[0][2]=0;
        a[1][0]=1;a[1][1]=0;a[1][2]=0;
        a[2][0]=mod-1;a[2][1]=0;a[2][2]=1;
    }
};
matrix operator *(matrix aa,matrix bb)
{
    ll a[3][3];
    for(int i=0;i<3;i++)
    for(int j=0;j<3;j++)
    {
        a[i][j]=0;
        for(int k=0;k<3;k++)
        {
            a[i][j]+=(ll)aa.a[i][k]*bb.a[k][j]%mod;
            a[i][j]%=mod;
        }
    }
 
    matrix cc;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
        {
            cc.a[i][j]=a[i][j];
        }
    return cc;
}
matrix matpow(matrix x,ll y)
{
    matrix res;res.one();
    while(y)
    {
        if(y&1){
            res=res*x;
        }
        x=x*x;
        y>>=1;
    }
    return res;
}
int vis[maxn];
int main()
{
    //freopen("rabbit20.in","r",stdin);
    scanf("%lld%d%d",&n,&k,&mod);
    for(int i=1;i<k;i++) inv[i]=ni(i);
 
    firstap();
    for(int i=0;i<k;i++)
    {
        if(!inv[i]||!pos[inv[i]])
        {
            next[i]=-1;len[i]=oo;
        }
        else
        {
            int kk=pos[inv[i]];
            next[i]=(ll)fib[kk-1]*i%k;
            len[i]=kk+1;
        }
    }
    int cirs;
    for (cirs=1;cirs!=-1 && !vis[cirs];cirs=next[cirs])
        vis[cirs]=1;
    matrix use;
    use.one();
    matrix AA,BB; AA.A();BB.B();
    for(int i=1;i!=cirs;i=next[i])
    {
        if(n<len[i])
        {
            use=use*matpow(AA,n);
            n=0;
            break;
        }
        else
        {
            use=use*matpow(AA,len[i]-1);
            use=use*BB;
            n-=len[i];
            if(n==0)
            {
                break;
            }
        }
    }
    if(n>0)
    {
        int cur = cirs;
        ll totL = 0;
        matrix matC;matC.one();
        do
        {
            matC = matC*matpow(AA,len[cur]-1);
            matC = matC*BB;
            totL += len[cur];
            cur = next[cur];
        }
        while (cur != cirs);
        use=use*matpow(matC, n / totL);
        n=n%totL;
        for(int i=cirs;;i=next[i])
        {
            if(n<len[i])
            {
                use=use*matpow(AA,n);
                break;
            }
            else
            {
                use=use*matpow(AA,len[i]-1);
                use=use*BB;
                n-=len[i];
                if(n==0)
                {
                    break;
                }
            }
        }
    }
    ll res=0;
    res=(ll)use.a[1][0]+(ll)use.a[2][0];
    res= res%mod;
    cout << res << endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值