【51nod 1126】求递推序列的第N项 【矩阵快速幂】or【枚举找循环节】

有一个序列是这样定义的:f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.
给出A,B和N,求f(n)的值。
Input
输入3个数:A,B,N。数字之间用空格分割。(-10000 <= A, B <= 10000, 1 <= N <= 10^9)
Output
输出f(n)的值。
Input示例
3 -1 5
Output示例
6

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
const int MAXN =100+10;
const LL mod=7;
struct Matrix{
    LL a[MAXN][MAXN];
    int r,c;
};
Matrix ori,res;
LL a,b,n;
void init(){
    memset(res.a,0,sizeof(res.a));
    res.r=2;res.c=2;
    for(int i=1;i<=2;i++)  //将结果矩阵赋值为单位矩阵
        res.a[i][i]=1;//单位矩阵A就是 [未知矩阵]B*A=B;
    ori.r=ori.c=2;
    ori.a[1][1]=a; ori.a[1][2]=b; ori.a[2][1]=1;// 初始化过度矩阵
    ori.a[2][2]=0;
}
Matrix multi(Matrix x,Matrix y){
    Matrix z;
    memset(z.a,0,sizeof(z.a));
    z.r=x.r;  z.c=y.c;  
    for(int i=1;i<=x.r;i++){
        for(int k=1;k<=x.c;k++){
            if(x.a[i][k]==0) continue;//小优化
            for(int j=1;j<=y.c;j++)
                 z.a[i][j]=(z.a[i][j]+(x.a[i][k]*y.a[k][j]%mod))%mod;
        }
    }
    return z;
}
void Matrix_mod(int n){
    while(n){
        if(n&1) res=multi(ori,res);
        ori=multi(ori,ori);
        n>>=1;
    }
    Matrix it;it.r=2;it.c=1;   //  初始矩阵
    it.a[1][1]=1; it.a[2][1]=1;

    res=multi(res,it); // 初始矩阵*过渡矩阵的幂 就是结果 
    printf("%lld\n",(res.a[2][1]%mod+mod)%mod);//  因为有可能是负数,所以这样处理
}
int main(){
        scanf("%lld%lld%lld",&a,&b,&n);
        init();
        Matrix_mod(n-1);  // 根据构造可知 n-1的幂才是f[n] ; 
    return 0;
}

根据鸽巢原理,循环节最大就是49. 。
所以我们知道这个是有规律的,我们可以找到其循环节然后来处理。
这里写图片描述
还有就是负数的情况不能够考虑,所以就有了(()%7+7)%7的操作。
代码

#include <cstdio>
int f[60];
int main(){
    int a,b,n;
    scanf("%d%d%d",&a,&b,&n);
    f[1] = f[2] = 1;
    for (int i = 3;i <= 50; ++i){
        f[i] = (a*f[i-1]+b*f[i-2])%7+7;
        f[i]%=7;
    }
    int f1,f2,flag = 0;
    for (int i = 1; i < 50; ++i){// 枚举找到循环节
        for (int j = i+1;j<=50; ++j){
            if (f[j]==f[i]&&f[j+1]==f[i+1]){
                f1 = i;
                f2 = j;
                flag = 1;
                break;
            }
        }
        if (flag) break;
    }
    if (n<f1){
        printf("%d\n",f[n]);
    }else printf("%d\n",f[f1+(n-f1)%(f2-f1)]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值