当前位置: 代码迷 >> 综合 >> Wannafly Winter Camp 2019 Day7 C 斐波那契数列 (矩阵快速幂)
  详细解决方案

Wannafly Winter Camp 2019 Day7 C 斐波那契数列 (矩阵快速幂)

热度:51   发布时间:2024-01-15 04:58:58.0

https://zhixincode.com/contest/24/problem/C
题意:定义 F i b n Fib_n Fibn?为斐波那契数列的第 n n n项,现在给定 R R R,求 ∑ n = 1 R ( F i b n & ( F i b n ? 1 ) ) \sum_{n=1}^{R}(Fib_n\&(Fib_{n}-1)) n=1R?(Fibn?&(Fibn??1))

这个题看上去和lowbit有关,因为对于一个数 x x x x & ( x ? 1 ) = x ? l o w b i t ( x ) x\&(x-1)=x-lowbit(x) x&(x?1)=x?lowbit(x),所以题目转变为求 F i b n Fib_n Fibn?的前 n n n项和 l o w b i t ( F i b n ) lowbit(Fib_n) lowbit(Fibn?)的前 n n n项和然后作差。
斐波那契数列的前 n n n项和 a n s 1 ans1 ans1可以通过矩阵快速幂求得,此处不做详细说明。
l o w b i t ( F i b n ) lowbit(Fib_n) lowbit(Fibn?)的前 n n n项和 a n s 2 ans2 ans2就没法通过矩阵快速幂求得了,但通过打表找规律其实可以发现规律(下图为前100项 l o w b i t ( F i b n ) lowbit(Fib_{n}) lowbit(Fibn?)的值)
前100项
循环周期为 6 6 6,且每个周期前 5 5 5项固定为 1 、 1 、 2 、 1 、 1 1、1、2、1、1 11211
而第 6 6 6项也是有规律的, 8 8 8出现的周期为 2 2 2 16 16 16出现的周期为 4 4 4 32 32 32出现的周期为 8 8 8……
4 ? 2 i 4*2^i 4?2i出现的周期为 2 i 2^i 2i i = 1 , 2 , 3 … … i=1,2,3…… i=1,2,3
这样就可以快速求出 l o w b i t ( F i b n ) lowbit(Fib_n) lowbit(Fibn?)的前 n n n项和 a n s 2 ans2 ans2了,最后的答案即 a n s 1 ? a n s 2 ans1-ans2 ans1?ans2

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;long long lowbit(long long x){
    return x&(-x);}
const long long MOD=998244353;
long long n;
long long b[100];
struct node
{
    long long g[5][5];
}f,res;
void init(node &x)//构造单位矩阵
{
    for(int i=1;i<=3;i++)for(int j=1;j<=3;j++)if(i==j)x.g[i][j]=1;else x.g[i][j]=0;
}
void multiple(node &x,node &y,node &z)//矩阵乘法运算
{
    memset(z.g,0,sizeof(z.g));for(int i=1;i<=3;i++)for(int j=1;j<=3;j++)if(x.g[i][j]){
    for(int k=1;k<=3;k++){
    z.g[i][k]+=x.g[i][j]*y.g[j][k];z.g[i][k]%=MOD;}}
}
void quickpow(long long k)//快速幂
{
    init(res);node temp=f,t;while(k){
    if(k&1){
    multiple(res,temp,t);res=t;}multiple(temp,temp,t);temp=t;k>>=1;}
}
long long solve()
{
    if(n<=1)return 1;quickpow(n-1);long long ret=res.g[1][1]*1+res.g[1][2]*1;ret%=MOD;ret+=res.g[1][3]*1;ret%=MOD;return ret;
}
int T;
int main()
{
    scanf("%d",&T);while(T--){
    scanf("%lld",&n);f.g[1][1]=1;f.g[1][2]=1;f.g[1][3]=0;f.g[2][1]=0;f.g[2][2]=1;f.g[2][3]=1;f.g[3][1]=0;f.g[3][2]=1;f.g[3][3]=0;long long ans1=solve();//printf("%lld\n",ans1);long long p=n/6;long long ans2=p*6%MOD;switch(n%6){
    case 1:ans2+=1;break;case 2:ans2+=2;break;case 3:ans2+=4;break;case 4:ans2+=5;break;case 5:ans2+=6;break;}b[0]=1;for(int i=1;i<=62;i++)b[i]=b[i-1]*2;for(int i=0;i<=62;i++){
    long long q=p-b[i];if(q<0) break;ans2+=(q/b[i+1]+1)*b[i]*8;ans2%=MOD;}//printf("%lld\n",ans2);printf("%lld\n",(ans1-ans2+MOD)%MOD);}return 0;
}