(牛客第一场)J.u's的影响力(矩阵快速幂,费马小定理)
第一天的影响力为,第二天的影响力为
,从第三天开始,每一天的影响力为前两天的影响力的乘积再乘以
的
次方。用数学语言描述:
。输出第n天的影响力,对1e9+7取模。
由于,所以用快速幂。
先推几个公式,找规律:
从第三项开始x的幂次为1,1,2,3,5...是斐波那契数列
的幂次为
是斐波那契数列
的幂次为
不是斐波那契数列,但为前两项之和+1
同时还需用到一个数论的结论:费马小定理。
费马小定理:为素数,对于任意整数
都有
。
若无法被
整除,则有
。
根据费马小定理,我们可以通过矩阵快速幂求得逆元,因为由上式有,不过跟这题没关系。
矩阵快速幂求斐波那契数列
由于数据范围很大,如果用递推求斐波那契数列显然会超时。
模板
typedef vector<int> vec; typedef vector<vector<int>> mat; typedef long long ll; const ll mod=1e9+7; // 首先求出两个矩阵乘积 mat mul(mat &A,mat &B){ mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) for(int k=0;k<B.size();k++) for(int j=0;j<B[0].size();j++) C[i][j]=(C[i][j]+A[i][k]*B[k][j])%mod; return C; } //算快速幂 mat poww(mat A,ll n){ mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();i++) B[i][i]=1; //相当于朴素快速幂里面res=1; 将B矩阵对角线元素初始化为1 while(n>0){ if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } void fib(int n){ mat A(2,vec(2)); A[0][0]=A[0][1]=A[1][0]=1; A[1][1]=0; A=poww(A,n-1); printf("%d",A[0][0]); //A[0][0] 对应斐波那契数列的第 n 项 }
题解代码
#include <iostream> #include <cstdio> #include <vector> using namespace std; typedef long long ll; typedef vector<ll> vec; typedef vector<vector<ll>> mat; const ll mod=1e9+6; //费马小定理,用的模数 const ll mod2=1e9+7; //答案所用模数 // 首先求出两个矩阵乘积 mat mul(mat &A,mat &B){ //用模数1,矩阵快速幂算的是x,y,a^b的幂,幂的模要用到费马小定理 mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) for(int k=0;k<B.size();k++) for(int j=0;j<B[0].size();j++) C[i][j]=(C[i][j]+A[i][k]*B[k][j]%mod)%mod; return C; } //算快速幂 mat poww(mat A,ll n){ mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();i++) B[i][i]=1; //相当于朴素快速幂里面res=1; 将B矩阵对角线元素初始化为1 while(n>0){ if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } ll poww(ll a,ll n){ //用模数2,算出最终答案 // 底数为0时,直接返回0, 否则会卡10% 的数据 if(a==0) return 0; ll res=1; while(n>0){ if(n&1) res=res*a%mod2; a=a*a%mod2; n>>=1; } return res; } int main(){ mat A(2,vec(2)); A[0][0]=A[0][1]=A[1][0]=1; A[1][1]=0; //矩阵1 /* 1 1 1 0 用来算斐波那契数,也就是x,y的模数 */ mat B(3,vec(3)); B={{1,1,1},{1,0,0},{0,0,1}}; //矩阵2 /* 1 1 1 1 0 0 0 0 1 用来算a^b的幂次 */ ll n,x,y,a,b; cin>>n>>x>>y>>a>>b; x%=mod2;y%=mod2; a%=mod2; //分别对mod2取模 if(n==1){ printf("%lld\n",x); //输出前两项 return 0; } if(n==2){ printf("%lld\n",y); return 0; } if(a==0||x==0||y==0){ //由前面费马小定理幂次同余模p的条件,a,x,y能直接被mod2整除,输出0 printf("0\n"); return 0; } a=poww(a,b); A=poww(A,n-2); B=poww(B,n-2); ll p=A[0][0], q=A[1][0], r=(B[1][0]+B[1][2])%mod;//分别对应y 的幂次,x的幂次,a^b的幂次 ll ans=(poww(x,q)*poww(y,p)%mod2*poww(a,r))%mod2; //快速幂求答案 printf("%lld\n",ans%mod2); return 0; }