wanxue

博客园 首页 新随笔 联系 订阅 管理
  7 随笔 :: 0 文章 :: 0 评论 :: 110 阅读

题目大意

现在穿T次手串,每根手串的长度分别为不同的n,有木和金两种珠子,相邻两颗珠子必须有一个是金。

题目思路分析

我们现在设穿到第n个珠子时用金的方案数为f[1][n],用木的方案数为f[0][n]
如果第n个珠子为金,那么前一颗珠子是什么都可以,因此f[1][n]=f[1][n-1]+f[0][n-1]
而如果第n个珠子为木,那么前一颗珠子必须是金,因此f[0][n]=f[1][n-1]

时间复杂度分析

如果使用纯递推,时间复杂度为O(Tn),如果Tn<=1e8的话,这道题可以轻易解决,然而,这道题n最大能到1e18,而T最大能到10,因此不能使用纯递推
因此需要把时间复杂度优化到O(Tlog2(n)),就要用到矩阵加速。
这道题不太方便使用通项公式,因为f[1][n]=f[1][n-1]+f[0][n-1]=f[1][n-1]+f[1][n-2],是一个斐波那契数列,而斐波那契数列的通项公式中有5这个无理数,可能会影响计算

所以总体思路就是矩阵快速幂加速递推

代码实现

矩阵乘法中 设A是一个P×M,B是一个M×Q,C是A乘以B的乘积,那么Ci,j=k=1MAi,k×Bk,j
利用这个性质,我们可以将递推式转化为

(f[0][i]f[1][i])×

(0111)

=(f[0][i+1]f[1][i+1])
如果第一个用的是木珠子的话,最后一个只能用金珠子,这个要注意一下

AC代码

#include<bits/stdc++.h>
#define N 5
#define mod 1000000007
using namespace std;
long long n;
struct arr{
    long long a[N][N];
    arr(){
        memset(a,0,sizeof(a));
    }
}base,rst;
arr operator *(const arr &a,const arr &b){
    arr rst;
    for(int k=1;k<=N;k++){
        for(int i=1;i<=N;i++){
            for(int j=1;j<=N;j++){
                rst.a[i][j]=(rst.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
            }
        }
    }
    return rst;
}//矩阵乘法
void qpow(long long b) {
    while (b) {
        if (b & 1) rst = rst * base;
        base = base * base;
        b >>= 1;
    }
}//矩阵快速幂
void func(){
	scanf("%lld",&n);
    rst.a[1][1]=1,base.a[1][1]=rst.a[1][2]=0;//很简单粗暴的初始化
    base.a[1][2]=base.a[2][2]=base.a[2][1]=1;
    qpow(n-1);
    long long ans=rst.a[1][2];
    base.a[1][1]=rst.a[1][1]=0,rst.a[1][2]=1;
    base.a[1][2]=base.a[2][2]=base.a[2][1]=1;
    qpow(n-1);
    ans=(ans+rst.a[1][1]+rst.a[1][2])%mod;
    printf("%lld\n",ans);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--)func();
}
posted on   thelatersnow  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示