HDU 5451 2015长春站网赛1002(数论)

附上大神的博客:广义斐波拉契数列循环节点
比赛的时候搞了两种想法:1:暴力找一遍循环节点,应该能在要求的复杂度内找到(并没有写)
2:这里写图片描述
这个时候我们的复杂度降到了p*log(2^(32)),然后天真地写了,结果wa了4发,感觉应该不会贵,待会附上代码(依旧TLE)

说说正解:广义斐波拉契数列的循环节:(p-1)*(p+1);
那么问题就转化成了[A]^((p-1)*(p+1)),这个东西直接一次矩阵的快速幂就搞定了,蛋蛋的忧伤,因为这题没有了名额。。。哎。。。

AC代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 2

int MOD;
LL p;

struct Matrix{
    LL mat[N][N];
    ///两个矩阵乘法
    Matrix operator *(const Matrix& a) const{
        Matrix c;
        memset(c.mat,0,sizeof(c.mat));
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                for(int k=0;k<N;k++){
                    LL ssss = ((mat[i][k]*a.mat[k][j])%MOD+MOD)%MOD;
                    c.mat[i][j] = ((c.mat[i][j]+ssss)%MOD+MOD)%MOD;
                }
            }
        }
        return c;
    }
    ///矩阵的幂,用二分的思想写,就是快速幂算法
    Matrix operator ^(LL n) const{
        Matrix c;
        ///初始化为单位矩阵
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                c.mat[i][j]=(i==j);
            }
        }
        ///就是快速幂
        Matrix a = *this;///毕竟直接敲的模板,没运行就写上去了,发现这里少了一个*号
        while(n)
        {
            if(n%2) c=c*a;
            a=a*a;
            n=n>>1;
        }
        return c;
    }
};

Matrix ss;

///a^n%m
LL quickpow(LL a,LL n,int m){
    LL ret = 1;
    while(n){
        if(n&1) ret = (ret*a)%m;
        a = (a*a)%m;
        n >>= 1;
    }
    return ret;
}

int main(){
    //freopen("test.in","r",stdin);
    int T,tCase = 0;
    scanf("%d",&T);
    ss.mat[0][0] = 10; ss.mat[0][1] = -1; ss.mat[1][0] = 1; ss.mat[1][1] = 0;
    while(T--){
        printf("Case #%d: ",++tCase);
        LL x;
        scanf("%I64d%d",&x,&MOD);
        p = (MOD-1)*(MOD+1);
        LL r = quickpow(2,x,p);
        Matrix st;
        st.mat[0][0] = 10; st.mat[0][1] = -1; st.mat[1][0] = 1; st.mat[1][1] = 0;
        st = st^r;
        LL aaaa = (((st.mat[0][0]*10)%MOD+MOD)%MOD + ((st.mat[0][1]*2)%MOD+MOD)%MOD)%MOD;
        if(!aaaa){
            printf("%d\n",MOD-1);
        }
        else
            printf("%I64d\n",aaaa-1);
    }
    return 0;
}

下面是TLE代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 2

int MOD;

struct Matrix{
    LL mat[N][N];
    ///两个矩阵乘法
    Matrix operator *(const Matrix& a) const{
        Matrix c;
        memset(c.mat,0,sizeof(c.mat));
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                for(int k=0;k<N;k++){
                    LL ssss = ((mat[i][k]*a.mat[k][j])%MOD+MOD)%MOD;
                    c.mat[i][j] = ((c.mat[i][j] + ssss)%MOD+MOD)%MOD;
                }
            }
        }
        return c;
    }
    ///矩阵的幂,用二分的思想写,就是快速幂算法
    Matrix operator ^(LL n) const{
        ///就是快速幂
        Matrix a = *this;///毕竟直接敲的模板,没运行就写上去了,发现这里少了一个*号
        while(n--)
        {
            a=a*a;
        }
        return a;
    }
};

Matrix ans;

int main(){
    //freopen("test.in","r",stdin);
    //freopen("out.txt","w",stdout);
    int T,tCase = 0;
    scanf("%d",&T);
    while(T--){
        printf("Case #%d: ",++tCase);
        LL p;
        scanf("%I64d%d",&p,&MOD);
        Matrix st;
        st.mat[0][0] = 10; st.mat[0][1] = -1; st.mat[1][0] = 1; st.mat[1][1] = 0;
        ans.mat[0][0] = 1; ans.mat[0][1] = 0; ans.mat[1][0] = 0; ans.mat[1][1] = 1;
        if(MOD == 2){
            printf("1\n");
            continue;
        }
        if(!p)  {printf("97\n");continue;}
        while(p){
            LL x = p/MOD,y = p%MOD;
            Matrix res;
            res = st^y;
            ans = ans*res;
            st = st^x;
            p /= MOD;
        }
        LL ppp = (((ans.mat[0][0] * 10)%MOD+MOD)%MOD + ((ans.mat[0][1]*2)%MOD+MOD)%MOD)%MOD;
        if(!ppp){
            printf("%I64d\n",MOD-1);
        }
        else printf("%I64d\n",ppp-1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值