矩陣快速冪模板+例題(快速入門)
本來早在一個月前就應該把這給掌握的,硬是得等到卡題了,纔想到來補知識
模板
單純的求某一矩陣的快速冪是沒有什麼意義的,主要是由於所求的遞推式的數比較大時,如果使用暴力遞推,必定會超時,這時矩陣快速冪的作用就完美體現了
根據個人碼風和編碼習慣,敲了一份模板,如果覺得有用,大佬們不妨留個讚唄(手動滑稽)
/*
* 矩陣快速冪 n*n矩陣的x次冪
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
#define mod 9973
using namespace std;
int n,x;
const int maxn=15;
struct mat{
ll m[maxn][maxn];
mat(){
memset(m,0,sizeof(m));
}
//重載矩陣乘法
mat operator * (const mat b)const{
mat ans;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
ans.m[i][j]+=m[i][k]*b.m[k][j];
ans.m[i][j]%=mod;
}
}
}
return ans;
}
};
//矩陣快速冪
mat pow_mat(mat a,int b){
mat ans;
//初始化爲單位矩陣
for(int i=1;i<=n;i++){
ans.m[i][i]=1;
}
while(b){
if(b&1){
ans=ans*a;
}
a=a*a;
b>>=1;
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&x);
mat a;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&a.m[i][j]);
}
}
a=pow_mat(a,x);
//輸出矩陣
/*
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a.m[i][j]<<" ";
}
cout<<endl;
}*/
ll sum=0;
for(int i=1;i<=n;i++){
sum=(sum+a.m[i][i])%mod;
}
printf("%lld\n",sum);
}
return 0;
}
例題
一、HDU 1575:Tr A
題目大意:
題意也是很簡單,畢竟是模板題嘛;求某一矩陣a的k次冪,之後求矩陣對角線的和取模
代碼:
剛剛我的模板恰好是解決這一題的完整代碼
二、POJ 3070:Fibonacci
題目大意:
求最經典的斐波拉契數列f[n],但由於n比較大,如果我們直接使用遞歸的話會超時,但是我們選擇用數組存的話,內存肯定會超限(因爲n過於大)
這時就得想到矩陣快速冪了
代碼:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
#define inf 0x3f3f3f3f
#define mod 10000
#define ll long long
using namespace std;
const double eps=1e-6;
const int maxn=5;
ll n,x;
struct mat{
ll m[maxn][maxn];
mat(){
memset(m,0,sizeof(m));
}
mat operator * (const mat b)const{
mat ans;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
ans.m[i][j]+=m[i][k]*b.m[k][j];
ans.m[i][j]%=mod;
}
}
}
return ans;
}
};
mat pow_mat(mat a,int b){
mat ans;
for(int i=1;i<=n;i++){
ans.m[i][i]=1;
}
while(b){
if(b&1){
ans=ans*a;
}
a=a*a;
b>>=1;
}
return ans;
}
int main(){
n=2;
while(~scanf("%lld",&x)){
if(x==-1)
break;
mat a;
a.m[1][1]=1,a.m[1][2]=1;
a.m[2][1]=1,a.m[2][2]=0;
a=pow_mat(a,x);
printf("%lld\n",a.m[1][2]);
}
return 0;
}
三、HNUCM 1620: Fy’s dota2
題目大意:
很明顯能夠得到遞推公式:
由於所求的數列下標比較大,於是用到快速冪,構造矩陣
代碼:
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define mod 7777777
#define ll long long
using namespace std;
const double eps=1e-6;
const int maxn=15;
int n,x;
struct mat{
ll m[maxn][maxn];
mat(){
memset(m,0,sizeof(m));
}
//重載矩陣乘法
mat operator * (const mat b)const{
mat ans;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
ans.m[i][j]+=m[i][k]*b.m[k][j];
ans.m[i][j]%=mod;
}
}
}
return ans;
}
};
//矩陣快速冪
mat pow_mat(mat a,int b){
mat ans;
for(int i=1;i<=n;i++){
ans.m[1][i]=pow(2,n-i);
}
while(b){
if(b&1){
ans=ans*a;
}
a=a*a;
b>>=1;
}
return ans;
}
int main(){
scanf("%d%d",&n,&x);
if(n>=x){
printf("%d\n",(int)pow(2,x-1));
}
else{
mat a;
for(int i=1;i<=n;i++){
a.m[i][1]=1;
a.m[i][i+1]=1;
}
a=pow_mat(a,x-n);
printf("%lld\n",a.m[1][1]);
}
return 0;
}