[HDU 5667] Sequence (矩阵快速幂+费马小定理)

HDU - 5667
题意很简单,给你一个递推式
F1=1
F2=ab
Fn=abFcn1Fn2(n>2)

F(n)modp

其中 N可以高达 1e18,这赤裸裸地就告诉你解法了,矩阵快速幂搞起

不过这题矩阵需要一点技巧,首先观察可得 Fn 都是 a的幂次
所以我们只需要算指数 Gn 就好了,最后再快速幂算 aGn

然而指数可能比较大,所以我们要用到费马小定理降幂
ap11(modp) ,其中 p是质数,a要与 p互质
所以 a^(r+k*(p-1))=a^(r) (mod p)
所以对指数 modp1 就好了
注意点,也是这题坑点,就是要特判一下 amodp=0 的情况
因为使用费马小定理的前提是, a和 p要互质

然后构造矩阵即可
0101c00b1Gn2Gn11=Gn1Gn1

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

struct Matrix
{
    static int MOD;
    int siz;
    int n[5][5];
    Matrix(int tsiz=0):siz(tsiz){CLR(n);}

    void E(){CLR(n);for(int i=0; i<siz; i++) n[i][i]=1;}

    Matrix operator * (const Matrix &v) const
    {
        Matrix tem(siz);
        for(int i=0; i<siz; i++) for(int j=0; j<siz; j++) for(int k=0; k<siz; k++)
            tem.n[i][j]=(tem.n[i][j]+(LL)n[i][k]*v.n[k][j])%MOD;
        return tem;
    }
};
int Matrix::MOD;

LL N;
int A,B,C,P;

int spPow(int,int,int);

int main()
{
    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%lld%d%d%d%d", &N, &A, &B, &C, &P);
        if(N==1){puts("1");continue;}
        if(N==2){printf("%d\n", spPow(A,B,P));continue;}
        if(A%P==0){puts("0");continue;}
        N-=2;
        Matrix::MOD=P-1;
        Matrix ans(3),tem(3);
        ans.E();
        tem.n[0][0]=0;tem.n[0][1]=1;tem.n[0][2]=0;
        tem.n[1][0]=1;tem.n[1][1]=C;tem.n[1][2]=B;
        tem.n[2][0]=0;tem.n[2][1]=0;tem.n[2][2]=1;
        while(N)
        {
            if(N&1) ans=ans*tem;
            tem=tem*tem;
            N>>=1;
        }
        int tim=((LL)B*ans.n[1][1]+(LL)ans.n[1][2])%(P-1);
        printf("%d\n", spPow(A,tim,P));
    }
    return 0;
}

int spPow(int tem,int tim,int MOD)
{
    int res=1;
    while(tim)
    {
        if(tim&1) res=((LL)res*tem)%MOD;
        tem=((LL)tem*tem)%MOD;
        tim>>=1;
    }
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值