9.快速幂与矩阵快速幂

模板

快速幂

ll qpow(ll a,ll b)
{
	ll ans=1;
	while(n)
	{
		if(n&1)	ans=ans*a%mod;
		n>>=1;
		a=a*a%mod;
	}
	return ans;
}

矩阵快速幂

//自定义一个矩阵类型 
struct Matrix
{
	ll m[5][5];
};

//矩阵乘法 
Matrix multi(Matrix a,Matrix b)
{
	Matrix ans;
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			for(int k=0;k<5;k++)
			{
				ans.m[i][j]+=a.[i][k]*b.[i][k];
				ans.m[i][j]%=mod;
			}	
		}
	}
	return c;
}

//矩阵快速幂,与普通快速幂相似,只是把乘法改为了矩阵乘法 
Matrix qpow(Matrix a,ll n)
{
	Matrix ans;
	for(int i=0;i<5;i++)	ans.m[i][j]=1;
	while(n)
	{
		if(n&1)	ans=multi(ans,a);
		n>>=1;
		a=multi(a,a);
	}
	return ans;
}

取模时遇到负数

先%mod再+mod最后再%mod

Matrix multi(Matrix a,Matrix b)
{
	Matrix res;
	memset(res.m,0,sizeof(res.m));
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			for(int k=0;k<2;k++)
			{
				res.m[i][j]=(res.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
				res.m[i][j]=(res.m[i][j]+mod)%mod;
			}
		}
	}
	return res;
}

快速乘

这里是转载的这位博主的博客
(好难啊看不懂呜呜呜)(小声bb)
因为我们知道乘法有的时候会溢出,即使是 long long 也可能在乘法时因为结果过大溢出(当模数也是 long long )。所以我们需要寻找一种能高效完成乘法操作并且不会爆 long long 的算法,也就是快速乘。

ll ksc(ll x,ll y,ll p){
	ll z=(ld)x/p*y;
	ll res=(ull)x*y-(ull)z*p;
	return (res+p)%p;
}
// ll 表示 long long
// ld 表示 long double
// ull 表示 unsigned long long

练习

A - Fibonacci⭐(基础)

菲波那契数列是指这样的数列: 数列的第一个是0和第二个数是1,接下来每个数都等于前面2个数之和。 给出一个正整数a,要求菲波那契数列中第a个数的后四位是多少。
Input
多组数据 -1结束 范围1~10^9
Output
第x项的后4位
Sample Input
0
9
999999999
1000000000
-1
Sample Output
0
34
626
6875
思路:
这道题与前几天学递推时一样都是求斐波那契数列第n项,可是本题n的范围能达到1e9,显然不能再用递推写了。
f(n)=b的第一行*c的第一列,c是已知的,可以先算出矩阵幂,最后再与c相应相乘,至于a是怎么推出来的…多试试⑧😶😶
AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod=10000;
struct Matrix
{
	ll m[2][2];
};
Matrix multi(Matrix a,Matrix b)
{
	Matrix c;
	memset(c.m,0,sizeof(c.m));
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			for(int k=0;k<2;k++)
			{
				c.m[i][j]+=a.m[i][k]*b.m[k][j];
				c.m[i][j]%=mod;
			}
		}
	}
	return c;
}
Matrix qpow(ll n)
{
	Matrix ans;
	memset(ans.m,0,sizeof(ans.m));
	for(int i=0;i<2;i++)
		ans.m[i][i]=1;
	Matrix a;
	a.m[0][0]=1;
	a.m[0][1]=1;
	a.m[1][0]=1;
	a.m[1][1]=0;
	while(n)
	{
		if(n&1)	ans=multi(ans,a);
		n>>=1;
		a=multi(a,a);
	}
	return ans;
}
int main()
{
	ll n;
	while(scanf("%lld",&n)!=EOF)
	{
		if(n==-1)	break;
		else if (n==0)	printf("0\n");
		else if (n==1||n==2)	printf("1\n");
		else
		{
			Matrix res;
			res=qpow(n-1);
			printf("%lld\n",res.m[0][0]%mod);
		}
	}
	return 0;
}

B - Tr A

A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%9973。
Input
数据的第一行是一个T,表示有T组数据。
每组数据的第一行有n(2 <= n <= 10)和k(2 <= k < 10^9)两个数据。接下来有n行,每行有n个数据,每个数据的范围是[0,9],表示方阵A的内容。
Output
对应每组数据,输出Tr(A^k)%9973。
Sample Input
2
2 2
1 0
0 1
3 99999999
1 2 3
4 5 6
7 8 9
Sample Output
2
2686
当然是直接套板子啦
本来看到求对角线之和以为直接遍历会爆int啊超时啊bulabula一堆问题心想一定又要用到什么奇奇怪怪·我不知道·巨难·dddd只有我不懂·小定理,还是不死心想wa一发居然AC了😶😶
好⑧是我内心戏太多了呜呜呜这可真是个好题是我错怪你了

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int mod=9973;
struct Matrix
{
	ll m[15][15];
};
int t,l;
ll k;
Matrix multi(Matrix a,Matrix b)
{
	Matrix ans;
	memset(ans.m,0,sizeof(ans.m));
	for(int i=0;i<l;i++)
	{
		for(int j=0;j<l;j++)
		{
			for(int k=0;k<l;k++)
			{
				ans.m[i][j]+=a.m[i][k]*b.m[k][j];
				ans.m[i][j]%=mod;
			}
		}
	}
	return ans;
}
Matrix qpow(Matrix a,ll n)
{
	Matrix ans;
	memset(ans.m,0,sizeof(ans.m));
	for(int i=0;i<l;i++)	ans.m[i][i]=1;
	while(n)
	{
		if(n&1)	ans=multi(ans,a);
		n>>=1;
		a=multi(a,a);
	}
	return ans;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%lld",&l,&k);
		Matrix a;
		memset(a.m,0,sizeof(a.m));
		for(int i=0;i<l;i++)
		{
			for(int j=0;j<l;j++)
				scanf("%d",&a.m[i][j]);
		}
		a=qpow(a,k);
		ll sum=0;
		for(int i=0;i<l;i++)	sum=(sum+a.m[i][i])%mod;
		printf("%lld\n",sum);
	}
	return 0;
}

C - A Simple Math Problem ⭐⭐

Lele now is thinking about a simple function f(x).

If x < 10 f(x) = x.
If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);
And ai(0<=i<=9) can only be 0 or 1 .

Now, I will give a0 ~ a9 and two positive integers k and m ,and could you help Lele to caculate f(k)%m.
Input
The problem contains mutiple test cases.Please process to the end of file.
In each case, there will be two lines.
In the first line , there are two positive integers k and m. ( k<2*10^9 , m < 10^5 )
In the second line , there are ten integers represent a0 ~ a9.
Output
For each case, output f(k) % m in one line.
Sample Input
10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0
Sample Output
45
104
思路:
与A题类似啦只不过这次要cai出来的矩阵是个10*10的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
int mod;
int a[10]={0};
ll k;
struct Matrix
{
	int m[15][15];
};
Matrix multi(Matrix a,Matrix b)
{
	Matrix ans;
	memset(ans.m,0,sizeof(ans.m));
	for(int i=0;i<10;i++)
	{
		for(int j=0;j<10;j++)
		{
			for(int k=0;k<10;k++)
			{
				ans.m[i][j]+=a.m[i][k]*b.m[k][j];
				ans.m[i][j]%=mod;
			}
		}
	}
	return ans;
}
Matrix qpow(Matrix a,ll n)
{
	Matrix ans;
	memset(ans.m,0,sizeof(ans.m));
	for(int i=0;i<10;i++)	ans.m[i][i]=1;
	while(n)
	{
		if(n&1)	ans=multi(ans,a);
		n>>=1;
		a=multi(a,a);
	}
	return ans;
}
int main()
{
	while(scanf("%lld%d",&k,&mod)!=EOF)
	{
		Matrix res;
		memset(res.m,0,sizeof(res.m));
		for(int i=0;i<10;i++)	scanf("%d",&res.m[0][i]);
		if(k<10)	printf("%d\n",k%mod);
		else
		{
			for(int i=1;i<10;i++)	res.m[i][i-1]=1;
			res=qpow(res,k-9);
			ll ans=0;
			for(int i=0;i<10;i++)
				ans=(ans+(10-i-1)*res.m[0][i])%mod;
			printf("%lld\n",ans);
		}
	}
	return 0;
}

E - ProjectEuler 48

输入n,求

 1^1+2^2+3^3++n^n 

10^10 取模的结果。
Input
输入第一行组数T, 接下来T行,每行一个整数n。 (1 <= T <= 1000) (1 <= N <= 1000)
Output
对于每组数据,输出一个数,表示求和对10000000000取模的结果。
思路:
这道题mod是long long,普通快速幂取模的方法非常非常非常容易溢出,所以考虑快速乘或者换普通求幂的方式。
AC代码:
快速乘写法:(90ms)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ll mod=1e10;
//快速乘 (防止乘法计算模数也是long long时结果过大溢出) 
ll ksc(ll x,ll y,ll p)
{
	ll z=(ld)x/p*y;
	ll res=(ull)x*y-(ull)z*p;
	return (res+p)%p;
}
ll qpow(int n)
{
	ll ans=1,a=n;
	while(n)
	{
		if(n&1)	ans=ksc(ans,a,mod);
		n>>=1;
		a=ksc(a,a,mod);
	}
	return ans;
}
ll sum(int n)
{
	ll ans=0;
	for(int i=1;i<=n;i++)
		ans=(ans+qpow(i))%mod;
	return ans;
}
int main()
{
	int t;
	scanf("%d",&t);
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		printf("%lld\n",sum(n)%mod);
	}
	return 0;
}

普通写法:(800ms)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ll mod=1e10;

ll pow(int n)
{
	ll ans=1,a=n;
	while(n--)
	{
		ans=ans*a;
		ans%=mod;
	}
	return ans;
}
ll sum(int n)
{
	ll ans=0;
	for(int i=1;i<=n;i++)
		ans=(ans+pow(i))%mod;
	return ans;
}
int main()
{
	int t;
	scanf("%d",&t);
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		printf("%lld\n",sum(n)%mod);
	}
	return 0;
}

F - 求递推序列的第N项⭐

有一个序列是这样定义的:f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.
给出A,B和N,求f(n)的值。
Input
输入3个数:A,B,N。数字之间用空格分割。(-10000 <= A, B <= 10000, 1 <= N <= 10^9)
Output
输出f(n)的值。
AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int mod=7;
typedef long long ll;
int a,b;
ll n;
struct Matrix
{
	ll m[2][2];
};
Matrix multi(Matrix a,Matrix b)
{
	Matrix res;
	memset(res.m,0,sizeof(res.m));
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			for(int k=0;k<2;k++)
			{
				res.m[i][j]=(res.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
				res.m[i][j]=(res.m[i][j]+mod)%mod;
			}
		}
	}
	return res;
}
Matrix qpow(Matrix a,ll k)
{
	Matrix res;
	memset(res.m,0,sizeof(res.m));
	for(int i=0;i<2;i++)	res.m[i][i]=1;
	while(k)
	{
		if(k&1)	res=multi(res,a);
		k>>=1;
		a=multi(a,a);
	}	
	return res;
}
int main()
{
	Matrix ans;
	scanf("%d%d%lld",&a,&b,&n);
	ans.m[0][0]=a;
	ans.m[0][1]=b;
	ans.m[1][0]=1;
	ans.m[1][1]=0;
	ans=qpow(ans,n-2);
	if(n==1||n==2)	printf("1\n");
	else
		printf("%d\n",((ans.m[0][0]+ans.m[0][1])%mod+mod)%mod);
	return 0;
}

G - 人见人爱A^B

求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”
Input
输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。
Output
对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。
AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod=1000;
ll a,b,ans;
ll qpow(ll x,ll n)
{
	ll t=1;
	while(n)
	{
		if(n&1)	t=t*x%mod;
		n>>=1;
		x=x*x%mod;
	}
	return t;
}
int main()
{
	while(scanf("%lld%lld",&a,&b))
	{
		if(!a&&!b)	break;
		ans=qpow(a,b);
		printf("%lld\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值