遇到了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;
}