【知识学习】快速幂+矩阵快速幂+斐波那契数列及其推广

这一篇blog从有想法到写完所有代码总共花了好长好长时间

虽然最后可能在这篇博文里体现不出来,但是真的好难

快速幂和矩阵快速幂比较简单,稍微带过直接放代码

斐波那契数列才是这次的主角

快速幂

位运算比较快 用了>>和&

LL qpow(LL a , LL b) {
	if(b==0)
		return 1;
	LL c = qpow(a,b>>1)%k;
	if(b&1)
		return c*c%k*a%k;
	return c*c%k;
}

矩阵快速幂

重载一下乘法,再写几个结构体函数,其实其他和快速幂一样

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
#define PI acos(-1)
#define fin freopen("data.txt","r",stdin)
#define INF 2147483647
#define eps 1e-7
#define L 100005
#define mod 76
#define Fo(i,a,b) for(LL i=(a),_=(b); i<=_; i++)
#define Ro(i,b,a) for(LL i=(b),_=(a); i>=_; i--)
inline LL read() {
	LL x=0,f=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48ll),c=getchar();
    return x*f;
}

LL n , k;
struct Mat {
	LL num[105][105];
	Mat() {
		memset(num , 0 , sizeof(num));
	}
	void init() {
		Fo(i,1,n)
			num[i][i] = 1;
	}
	void print() {
		Fo(i,1,n) {
			Fo(j,1,n)
				printf("%d ",num[i][j]);
			printf("\n");
		}	
	}
};
Mat A , E;
Mat operator * (const Mat &x , const Mat &y) {
	Mat z;
	Fo(i,1,n)
		Fo(j,1,n)
			Fo(k,1,n)
				z.num[i][j]=(z.num[i][j]+x.num[i][k]*y.num[k][j]%mod)%mod;
	return z;
}

Mat qpow(Mat x , LL y) {
	if(y==0)
		return E;
	if(y==1)
		return x;
	Mat z = qpow(x , y>>1);
	if(y&1)
		return z*z*x;
	return z*z;
}

int main() {
	E.init();
	n=read();k=read();
	Fo(i,1,n)
		Fo(j,1,n)
			A.num[i][j]=read();
	qpow(A,k).print();
	return 0;
}

斐波那契数列及其推广

有几种形式:(目前遇到的,以下分别标号123)

f [ 1 ] = 1 , f [ 2 ] = 1 , f [ i ] = f [ i − 1 ] + f [ i − 2 ] f[1]=1,f[2]=1,f[i]=f[i-1]+f[i-2] f[1]=1,f[2]=1,f[i]=f[i1]+f[i2]
f [ 1 ] = 1 , f [ 2 ] = 1 , f [ i ] = A × f [ i − 1 ] + B × f [ i − 2 ] f[1]=1,f[2]=1,f[i]=A×f[i-1]+B×f[i-2] f[1]=1,f[2]=1,f[i]=A×f[i1]+B×f[i2]
f [ 1 ] = C , f [ 2 ] = D , f [ i ] = A × f [ i − 1 ] + B × f [ i − 2 ] f[1]=C,f[2]=D,f[i]=A×f[i-1]+B×f[i-2] f[1]=C,f[2]=D,f[i]=A×f[i1]+B×f[i2]

分别对应题目:
LG1720
HDU1005
WIT新生赛J题

第1种

就是常规的斐波那契数列,矩阵快速幂做法如下图

在这里插入图片描述
其实没必要非得用最后一行的公式,用倒数第二行的公式或许更能变通(比如第2种)

代码实现:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
#define PI acos(-1)
#define fin freopen("data.txt","r",stdin)
#define INF 2147483647
#define eps 1e-7
#define L 100005
#define mod 1000000007
#define Fo(i,a,b) for(LL i=(a),_=(b); i<=_; i++)
#define Ro(i,b,a) for(LL i=(b),_=(a); i>=_; i--)
#define Ms(a,b) memset((a),(b),sizeof(a))
inline LL read() {
	LL x=0,f=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48ll),c=getchar();
    return x*f;
}

LL n;
struct Mat {
	LL num[6][6];
	Mat() {
		Ms(num,0);
	}
	void init() {
		num[1][1]=num[1][2]=num[2][1]=1;
		num[2][2]=0;
	}
	void E() {
		Fo(i,1,n)
			num[i][i]=1; 
	}
	LL fib() {
		return num[2][1];
	}
};
Mat E , A;

Mat operator * (const Mat &x , const Mat &y) {
	Mat z;
	Fo(i,1,2)
		Fo(j,1,2)
			Fo(k,1,2)
				z.num[i][j]=z.num[i][j]+x.num[i][k]*y.num[k][j];
	return z;
}

Mat qpow(Mat x , LL y) {
	if(!y) return E;
	if(y==1) return x;
	Mat z=qpow(x,y>>1);
	if(y&1)
		return z*z*x;
	return z*z;
}

int main() {
	n=read();
	E.E(); A.init();
	printf("%lld.00",qpow(A,n).fib());
	return 0;
}

第2种

就是第1种的变通(画图有点丑)在这里插入图片描述
代码实现

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
#define PI acos(-1)
#define fin freopen("data.txt","r",stdin)
#define INF 2147483647
#define eps 1e-7
#define L 100005
#define mod 7
#define Fo(i,a,b) for(LL i=(a),_=(b); i<=_; i++)
#define Ro(i,b,a) for(LL i=(b),_=(a); i>=_; i--)
#define Ms(a,b) memset((a),(b),sizeof(a))
inline LL read() {
	LL x=0,f=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48ll),c=getchar();
    return x*f;
}

LL A , B , n;
struct Mat {
	LL num[15][15];
	Mat() {
		Ms(num,0);
	}
	void init(LL a , LL b) {
		num[1][1]=a; num[1][2]=1;
		num[2][1]=b; num[2][2]=0;
	}
	void K() {
		num[1][1]=num[1][2]=1;
	}
	void E() {
		Fo(i,1,n)
			num[i][i]=1;
	}
	LL fib() {
		return num[1][1]%mod;
	}
};
Mat E , K , P;

Mat operator * (const Mat &x , const Mat &y) {
	Mat z;
	Fo(i,1,2)
		Fo(j,1,2)
			Fo(k,1,2)
				z.num[i][j]=(z.num[i][j]+x.num[i][k]*y.num[k][j]%mod)%mod;
	return z;
}

Mat qpow(Mat x , LL y) {
	if(!y)  return E;
	if(y==1) return x;
	Mat z=qpow(x,y>>1);
	if(y&1)
		return z*z*x;
	return z*z; 
}

int main() {
	E.E(); K.K();
	while(1) {
		A=read(); B=read(); n=read();
		if(A==0&&B==0&&n==0)
			break;
		if(n==1||n==2) {
			printf("1\n");	
			continue;		
		}
		P.init(A,B);
		printf("%lld\n",(K*qpow(P,n-2)).fib()%mod);
	}
	return 0;
}

第3种

这个坑点有点多,但是学到一种求大数的斐波那契数列的方法

就是先求底数矩阵的个位数次方,然后底数矩阵自己求10次方,再做十位依次向前

因为我的做法是求得n-1次方,所以有很大的坑点

所以 直接用n就行!最好别写n-1因为要判断0啥的有亿点点麻烦
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
#define PI acos(-1)
#define fin freopen("data.txt","r",stdin)
#define INF 2147483647
#define eps 1e-7
#define L 100005
#define Fo(i,a,b) for(LL i=(a),_=(b); i<=_; i++)
#define Ro(i,b,a) for(LL i=(b),_=(a); i>=_; i--)
#define Ms(a,b) memset((a),(b),sizeof(a))
inline LL read() {
	LL x=0,f=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48ll),c=getchar();
    return x*f;
}

string n;
LL A , B , C , D , mod , len , num[500005] , flag;

struct Mat {
	int num[10][10];
	Mat() {
		Ms(num,0);
	}
	void K(LL a , LL b) {
		num[1][1]=a; num[1][2]=b;
		num[1][3]=num[2][1]=num[3][3]=1;
	} 
	void P(LL f0 , LL f1 , LL f2) {
		num[1][1]=f2;
		num[1][2]=num[2][1]=f1;
		num[2][2]=f0;
		num[3][1]=num[3][2]=num[3][3]=7;
	}
	void E() {
		Fo(i,1,3)
			num[i][i]=1;
	}
	LL fib() {
		return num[1][1]%mod;
	}
};
Mat K , P , ans;

Mat operator * (const Mat &x , const Mat &y) {
	Mat z;
	Fo(i,1,3)
		Fo(j,1,3)
			Fo(k,1,3)
				z.num[i][j]=(z.num[i][j]+x.num[i][k]*y.num[k][j]%mod)%mod;
	return z;
}

Mat E;
Mat qpow(Mat x , LL y) {
	if(!y) return E;
	if(y==1) return x;
	Mat z=qpow(x,y>>1);
	if(y&1)
		return z*z*x;
	return z*z;
}

int main() {
	A=read(); B=read(); C=read(); D=read(); cin>>n; mod=read();
	E.E(); K.K(A,B); P.P(D-C,C,D); ans.E();
	len = n.length();
	Fo(i,0,len-1)
		num[i+1]=n[i]-'0'; 
	Ro(i,len,1) {
		if(i==len) {
			LL top=i;
			while(num[top]==0) {
				top--;
				flag = 1;
			}
			if(!flag) {
				ans=qpow(K,num[i]-1);
				K=qpow(K,10);			
			} else {
				num[top]--;
				Fo(j,top+1,len)
					num[j]=9;
				ans = ans * qpow(K,num[i]);
				K = qpow(K,10);					
			}
			continue;
		}
		ans = ans * qpow(K,num[i]);
		K = qpow(K,10);			
	}
	ans=ans*P;
	printf("%lld",ans.fib()%mod);
	return 0;
}

好了,这篇blog会一直更新,遇到新的类型就会更新上去,希望能对你对我有所帮助

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cls1277

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值