一: 快速幂
快速乘
ll mul(ll a,ll b){
ll res=0;
while(b){
if(b&1) res=(res+a)%mod;
a=(a<<1)%mod;
b>>=1;
}
return res;
}
快速幂
int quick_pow(int a,int b){
int res=1;
while(b){
if(b&1) res=(ll)res*a%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
二: 矩阵快速幂
单位矩阵:从左上角到右下角均为
是
矩阵,
是
矩阵,则
是
矩阵,并且
矩阵乘法满足结合律,满足分配律,不满足交换律。
存储习惯:习惯使用结构体,在结构体中定义数组,定义矩阵的长和宽
struct node{
int m[N][N];
int x,y;
};
做题关键:
首先要设计转移矩阵,以斐波那契数列为例:
,因此我们设计转移矩阵就按照
前面的系数来写,因此转移矩阵为
,答案矩阵为
。
等于 转移矩阵
答案矩阵
,得到新的答案矩阵
,因此求斐波那契数列的第
项,则
,答案即为
例如:
,则转移矩阵为
,答案矩阵为
,因此
,
,所以最终答案矩阵为
,答案为
因此对于答案矩阵来说,经常是一个竖着的
的矩阵,其中
为我们得到
所要用到的参数个数,例如斐波那契数列即是
例题
1. A - Fibonacci
矩阵快速幂模板题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=10000;
struct node{
int m[3][3];
};
int n;
node mul(node a,node b){
node res;
memset(res.m,0,sizeof(res.m));
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
for(int k=1;k<=2;k++){
res.m[i][k]=(res.m[i][k]+a.m[i][j]*b.m[j][k]%mod)%mod;
}
}
}
return res;
}
node quick_pow(node a,int b){
node res;
memset(res.m,0,sizeof(res.m));
res.m[1][1]=1; res.m[2][2]=1;
while(b){
if(b&1) res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int main(){
while(scanf("%d",&n)){
if(n==-1) break;
node ans;
ans.m[1][1]=1; ans.m[1][2]=1; ans.m[2][1]=1; ans.m[2][2]=0;
ans=quick_pow(ans,n);
printf("%d\n",ans.m[2][1]);
}
return 0;
}
2. C - A Simple Math Problem
题目大意:
当 ,
当 ,
因此转移矩阵为,就是
,它们的下标都是从
开始的,矩阵里的也是。
初始答案矩阵为 ,得到最终的答案矩阵之前的一个矩阵是
,最终答案为
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
int m[15][15];
int x,y;
};
int n,mod;
node mul(node a,node b){
node res;
memset(res.m,0,sizeof(res.m));
res.x=a.x; res.y=b.y;
for(int i=1;i<=a.x;i++)
for(int j=1;j<=a.y;j++)
for(int k=1;k<=b.y;k++)
res.m[i][k]=(res.m[i][k]+(ll)a.m[i][j]*b.m[j][k]%mod)%mod;
return res;
}
node quick_pow(node a,int b){
node res;
memset(res.m,0,sizeof(res.m));
res.x=10; res.y=10;
for(int i=1;i<=10;i++) res.m[i][i]=1;
while(b){
if(b&1) res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int main(){
while(scanf("%d%d",&n,&mod)!=EOF){
node ans,a;
memset(a.m,0,sizeof(a.m));
a.x=10; a.y=10;
for(int i=1;i<=10;i++){
int x;
scanf("%d",&x);
a.m[1][i]=x;
a.m[i+1][i]=1;
}
if(n<=9){
printf("%d\n",n%mod);
continue;
}
a=quick_pow(a,(n-9));
memset(ans.m,0,sizeof(ans.m));
ans.x=10; ans.y=1;
for(int i=1;i<=10;i++) ans.m[i][1]=10-i;
ans=mul(a,ans);
printf("%d\n",ans.m[1][1]);
}
return 0;
}
3. D - Recursive sequence
此题给出递推式 ,让你求出
,已给出
此题难点在于转移矩阵的设计,前两个参数已由题目给出,分别为 ,现在就是解决如何使得
由与
相关的东西得到。由于
因此由上式可得我们需要用到 ,因此转移矩阵为
,初始答案矩阵
,最终的答案矩阵的前一个矩阵为
,得到最终答案矩阵为
注意开
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=2147493647;
struct node{
ll m[8][8];
int x,y;
};
node mul(node a,node b){
node res;
memset(res.m,0,sizeof(res.m));
res.x=a.x; res.y=b.y;
for(int i=1;i<=a.x;i++)
for(int j=1;j<=a.y;j++)
for(int k=1;k<=b.y;k++){
res.m[i][k]=(res.m[i][k]+(ll)a.m[i][j]*b.m[j][k]%mod)%mod;
}
return res;
}
node quick_pow(node a,int b){
node res;
memset(res.m,0,sizeof(res.m));
res.x=7; res.y=7;
for(int i=1;i<=7;i++) res.m[i][i]=1;
while(b){
if(b&1) res=mul(res,a);
a=mul(a,a); b>>=1;
}
return res;
}
int T;
int main(){
scanf("%d",&T);
ll p[8][8]=
{0,0,0,0,0,0,0,0,
0,1,2,1,4,6,4,1,
0,1,0,0,0,0,0,0,
0,0,0,1,4,6,4,1,
0,0,0,0,1,3,3,1,
0,0,0,0,0,1,2,1,
0,0,0,0,0,0,1,1,
0,0,0,0,0,0,0,1,
};
while(T--){
ll A,B,n;
scanf("%lld%lld%lld",&n,&A,&B);
node a,ans;
memset(a.m,0,sizeof(a.m));
a.x=7; a.y=7;
for(int i=1;i<=7;i++)
for(int j=1;j<=7;j++)
a.m[i][j]=p[i][j];
if(n==1){
printf("%d\n",A%mod);
continue;
}
else if(n==2){
printf("%d\n",B%mod);
continue;
}
a=quick_pow(a,(n-2));
memset(ans.m,0,sizeof(ans.m));
ans.x=7; ans.y=1;
ans.m[1][1]=B; ans.m[2][1]=A; ans.m[3][1]=16; ans.m[4][1]=8;
ans.m[5][1]=4; ans.m[6][1]=2; ans.m[7][1]=1;
ans=mul(a,ans);
printf("%lld\n",ans.m[1][1]);
}
return 0;
}