zoukankan      html  css  js  c++  java
  • 广义Fibonacci数列找循环节 学习笔记

    遇到了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;
    }
  • 相关阅读:
    mysql时间戳转换成可读时间格式
    浅谈依赖注入与控制反转
    win下安装wget以及使用wget
    Mysql 压力测试工具 mysqlslap
    PHP Web木马扫描器
    PHP双向队列,双端队列代码
    20款响应式bootstrap后台模板源码下载
    15款fullpage.js鼠标滚动页面动画展示特效
    20款网页常用的返回顶部代码
    21款大气的全屏图片切换代码
  • 原文地址:https://www.cnblogs.com/clno1/p/11516498.html
Copyright © 2011-2022 走看看