码迷,mamicode.com
首页 > 其他好文 > 详细

广义Fibonacci数列找循环节

时间:2019-09-13 13:31:09      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:void   ace   include   info   c++   fibonacci   return   memset   names   

遇到了2019ICPC南昌赛区的网络赛的一道题,fn=3*fn-1+2*fn-2,有多次询问求fn。总结起来其实就是在模P意义下,O(1)回答广义斐波那契额数列的第n项,可以说是一道模板题了。

这道题的解法有两种:①求出通项公式之后,用二次剩余+优化快速幂(可以k进制快速幂或者把快速幂分块)解决。②求出模P意义下的递推结果的循环节,然后给矩阵分块加速递推。

看到大佬说方法一因为受到二次剩余的局限(求出的根号可能在模P意义下开不了)并不是十分通用,这里就只提到了第二张办法。

首先是怎么求广义斐波那契额数列模P意义下的循环节呢?

这里给出https://blog.csdn.net/code92007/article/details/98109917这位大佬的办法

技术图片

 

 技术图片

 

 如果P是素数的话会简单一些

技术图片

 

 

ok,这道题求出循环节是(P-1)/2=499122176之后,因为有多组询问所以我们得想办法O(1)回答询问,关键在于怎么快速计算中间矩阵mat的n次幂mat^n,这里要用到一个矩阵分块的办法。

我们令块大小为kd=sqrt(循环节大小),那么我们让S数组计算mat^1->mat^kd,然后我们用P数组计算mat^kd,mat^2kd,mat^3kd....->mat^kd*kd,容易看到这个可以O(sqrt(n))计算得到,然后对于mat^n答案就是mat^(n%kd)*mat(n/kd)=S[n%kd]*P[n/kd],就可以O(1)得到了。

那么到这里此题可解了。但是要注意有些题会有卡常的情况,注意尽量少用Longlong(只在中间相乘用),加法用快速加......

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e7+10;
const LL MOD=998244353;
int qmod(int t) { return t<MOD ? t : t-MOD; }
struct matrix{
    int m[3][3];
    matrix() { memset(m,0,sizeof(m)); }
    friend matrix operator*(matrix a,matrix b) {
        matrix res;
        for (int i=1;i<=2;i++) for (int j=1;j<=2;j++) for (int k=1;k<=2;k++)
            res.m[i][j]=qmod(res.m[i][j]+(LL)a.m[i][k]*b.m[k][j]%MOD);
        return res;    
    }
};
LL Q,n,kd,ans[N],Ans; 

matrix c,S[100000],P[100000];  //分别是初始,小块,大块 
void prework() {
    S[0].m[1][1]=1; S[0].m[1][2]=0; S[0].m[2][1]=0; S[0].m[2][2]=1; P[0]=S[0];
    S[1].m[1][1]=0; S[1].m[1][2]=2; S[1].m[2][1]=1; S[1].m[2][2]=3;
    for (int i=2;i<=kd;i++) S[i]=S[i-1]*S[1];
    P[1]=S[kd];
    for (int i=2;i<=kd;i++) P[i]=P[i-1]*P[1];
    c.m[1][1]=0; c.m[1][2]=1; c.m[2][1]=0; c.m[2][2]=0;
}

LL solve(LL n) {
    matrix ret=c*S[n%kd]*P[n/kd];
    return ret.m[1][1];
}

int main()
{
    kd=(LL)sqrt(MOD); prework();
    cin>>Q>>n;
    for (int i=1;i<=Q;i++) {
        ans[i]=solve(n%499122176);
        Ans=Ans^ans[i];
        n=n^(ans[i]*ans[i]);
    }
    cout<<Ans<<endl;
    return 0;
}

 

广义Fibonacci数列找循环节

标签:void   ace   include   info   c++   fibonacci   return   memset   names   

原文地址:https://www.cnblogs.com/clno1/p/11516498.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!