矩阵快速幂(模板+构造)

31 篇文章 0 订阅

矩阵快速幂(模板+构造)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mod 1000000007
/*              ( x x x x x )^n
                ( x x x x x )
  (x x x x x) * ( x x x x x )
                ( x x x x x )
                ( x x x x x )         */
LL nn;
struct mat{
    LL m[10][10];
};
mat mat_mul(mat aa,mat bb,int nn){ 
    mat re;memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++){
        for(int j=0;j<nn;j++){
            for(int k=0;k<nn;k++){
                re.m[i][j]=(re.m[i][j]+aa.m[i][k]*bb.m[k][j])%mod;
            }
        }
    }
    return re;
}
mat mat_quick_mod(mat jin,LL x,int nn){
    mat re;memset(re.m,0,sizeof(re.m));
    for(int i=0;i<nn;i++) re.m[i][i]=1;
    while(x){
        if(x&1) re=mat_mul(re,jin,nn);
        x=x>>1;jin=mat_mul(jin,jin,nn);
    }
    return re;
}
bool mat_same(mat aa,mat bb,int n){
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            if(aa.m[i][j]!=bb.m[i][j])return 0;
    return 1;
}
void init_mat(mat &aa,int nn){//初始化为单位矩阵
    memset(aa.m,0,sizeof(aa.m));
    for(int i=0;i<nn;i++) aa.m[i][i]=1;
}
void jin_mat_init(){//乘的转移矩阵
    memset(xx.m,0,sizeof(xx.m));
    xx.m[0][0]=1;xx.m[0][1]=1;xx.m[0][2]=0;xx.m[0][3]=0;
    xx.m[1][0]=1;xx.m[1][1]=0;xx.m[1][2]=0;xx.m[1][3]=0;
    xx.m[2][0]=1;xx.m[2][1]=0;xx.m[2][2]=1;xx.m[2][3]=0;
    xx.m[3][0]=1;xx.m[3][1]=0;xx.m[3][2]=1;xx.m[3][3]=1;
}

先写出递推式,写出前一项或者后一项,然后发现所有的关键要素(递推式的不是相邻项,要把中间的项考虑进去),然后根据递推式写出矩阵。思路理清楚,处理好边界,处理好细节。(我喜欢多些一维,好理解,其实写多了都可以省掉)

http://www.51nod.com/Challenge/Problem.html#!#problemId=1126

1126 求递推序列的第N项

  1. 1 秒
  2. 131,072 KB
  3. 10 分
  4. 2 级题

有一个序列是这样定义的:f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.

给出A,B和N,求f(n)的值。

输入

输入3个数:A,B,N。数字之间用空格分割。(-10000 <= A, B <= 10000, 1 <= N <= 10^9)

输出

输出f(n)的值。

输入样例

3 -1 5

输出样例

6

 类似斐波那契。注意负数取余要加上mod再%mod。

#include <iostream>
using namespace std;
int n;
struct mat{
   int a[2][2];	
};
mat matmul(mat x,mat y){
   mat z;
   for(int i=0;i<2;i++){
   	for(int j=0;j<2;j++){
      	z.a[i][j]=((x.a[0][j]*y.a[i][0]+x.a[1][j]*y.a[i][1])%7+7)%7;
	   }
   }
   return z;	
}

mat matpow(mat x,mat y){
	
	while(n>0){
		if(n&1){
		  y=matmul(x,y);
		}
		n=n/2; 
		x=matmul(x,x);
	}
	return y;
}
int main(){
	int A,B;
	 cin >> A >> B >> n;
	mat re,c;
	n=n-2;
	c.a[0][0]=A;c.a[0][1]=1;
	c.a[1][0]=B;c.a[1][1]=0;
	re.a[0][0]=1;re.a[0][1]=0;
	re.a[1][0]=0;re.a[1][1]=1;
	re=matpow(c,re);
	cout << ((re.a[0][0]+re.a[1][0])%7+7)%7;
	return 0;
} 

 http://codeforces.com/contest/1117/problem/D

 

递推式:f(n)=f(n-1)+..+f(n-m)。

(多算了一维,好理解)

注意处理n<m。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
#define mod 1000000007
#define N 105
/*		( x x x x x )^n 
	        ( x x x x x )
  (x x x x x) * ( x x x x x )
		( x x x x x )
		( x x x x x )         */
LL n,m;
struct mat{
	LL m[N][N];
};
mat matmul(mat a,mat b){
	mat re;
	memset(re.m,0,sizeof(re.m));
	for(int i=1;i<=m+1;i++){
		for(int j=1;j<=m+1;j++){
			for(int k=1;k<=m+1;k++){
				re.m[i][j]=(re.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
			}	
		}
	}
	return re;
}
mat quickmod(LL x,mat jin){
	mat re;
	for(int i=1;i<=m+1;i++) re.m[i][i]=1;
	while(x){
		if(x&1)
			re=matmul(re,jin);
		x=x>>1;
		jin=matmul(jin,jin);
	}
	return re;
}
mat init(){
	mat x;
	memset(x.m,0,sizeof(x.m));
	x.m[1][1]=1;x.m[m][1]=1;
	for(int i=1;i<=m;i++){
		x.m[i][i+1]=1;
	}
	return x;
}
int main(){
	cin>>n>>m;
	if(n<m) {
		cout<<1;
		return 0;
	}
	mat t;
	memset(t.m,0,sizeof(t.m));
	for(int i=1;i<=m+1;i++)
		t.m[1][i]=1;
	t.m[1][1]++;
	mat mm=quickmod(n-m,init());
	mm=matmul(t,mm);
	cout<<mm.m[1][1];
	return 0;
}

 2016ACM/ICPC亚洲区沈阳站 //卡铜牌题

http://acm.hdu.edu.cn/showproblem.php?pid=5950

 

递推式:f(n)=f(n-1)+2*f(n-2)+i^4。

递推下一项是f(n+1)=f(n)+2*f(n-1)+(i+1)^4。

将上式展开,发现与连续三项有关,与i^4,i^3,i^2,i^1,i^0,有关。

然后推矩阵(多一维)

 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
#define mod 2147493647
#define N 20
/*                ( x x x x x )^n
                ( x x x x x )
  (x x x x x) * ( x x x x x )
                ( x x x x x )
                ( x x x x x )         */
LL nn;
struct mat{
    LL m[10][10];
};
mat matmul(mat a,mat b){
    mat re;
    memset(re.m,0,sizeof(re.m));
    for(int i=1;i<=nn;i++){
        for(int j=1;j<=nn;j++){
            for(int k=1;k<=nn;k++){
                re.m[i][j]=(re.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
            }
        }
    }
    return re;
}
mat quickmod(LL x,mat jin){
    mat re;
    memset(re.m,0,sizeof(re.m));
    for(int i=1;i<=nn;i++) re.m[i][i]=1;
    while(x){
        if(x&1)
            re=matmul(re,jin);
        x=x>>1;
        jin=matmul(jin,jin);
    }
    return re;
}
mat init(){
    mat x;
    x.m[1][1]=1;x.m[1][2]=1;x.m[1][3]=0;x.m[1][4]=0;x.m[1][5]=0;x.m[1][6]=0;x.m[1][7]=0;x.m[1][8]=0;
    x.m[2][1]=2;x.m[2][2]=0;x.m[2][3]=1;x.m[2][4]=0;x.m[2][5]=0;x.m[2][6]=0;x.m[2][7]=0;x.m[2][8]=0;
    x.m[3][1]=0;x.m[3][2]=0;x.m[3][3]=0;x.m[3][4]=0;x.m[3][5]=0;x.m[3][6]=0;x.m[3][7]=0;x.m[3][8]=0;
    x.m[4][1]=1;x.m[4][2]=0;x.m[4][3]=0;x.m[4][4]=1;x.m[4][5]=0;x.m[4][6]=0;x.m[4][7]=0;x.m[4][8]=0;
    x.m[5][1]=4;x.m[5][2]=0;x.m[5][3]=0;x.m[5][4]=4;x.m[5][5]=1;x.m[5][6]=0;x.m[5][7]=0;x.m[5][8]=0;
    x.m[6][1]=6;x.m[6][2]=0;x.m[6][3]=0;x.m[6][4]=6;x.m[6][5]=3;x.m[6][6]=1;x.m[6][7]=0;x.m[6][8]=0;
    x.m[7][1]=4;x.m[7][2]=0;x.m[7][3]=0;x.m[7][4]=4;x.m[7][5]=3;x.m[7][6]=2;x.m[7][7]=1;x.m[7][8]=0;
    x.m[8][1]=1;x.m[8][2]=0;x.m[8][3]=0;x.m[8][4]=1;x.m[8][5]=1;x.m[8][6]=1;x.m[8][7]=1;x.m[8][8]=1;
    return x;
}
int main(){
    int T;cin>>T;
    while(T--){
        LL n,a,b;
        cin>>n>>a>>b;
        if(n==1) {cout<<a<<endl;continue;}
        if(n==2) {cout<<b<<endl;continue;}
        nn=8;
        mat x;memset(x.m,0,sizeof(x.m));
        x.m[1][1]=(2*a+b+81)%mod;x.m[1][2]=b;x.m[1][3]=a;x.m[1][4]=81;x.m[1][5]=27;x.m[1][6]=9;x.m[1][7]=3;x.m[1][8]=1;
        mat t=quickmod(n-3,init());
        x=matmul(x,t);
        cout<<x.m[1][1]<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值