遇到了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; }
原文地址:https://www.cnblogs.com/clno1/p/11516498.html
时间: 2024-10-19 08:51:37