快速幂【快速幂】

快速幂:

//快速幂
#define ll long long
ll firstpow(ll x, ll pow)  //位运算
{
	ll result = 1;
	while (pow)
	{
		if (pow & 1)
		{
			result *= x; result %= mod;
		}
		pow = pow >> 1;
		x *= x; x %= mod;
	}
	return result;
}

不得不说,快速幂真的很爱出题。今天又做到了,get到几个新点。特此记录。

1、一个更简洁的代码

当然了,做法没有任何差别

ll power(ll a, int n)
{
    ll res = 1;
    for (; n; n /= 2, a *= a)
        if (n&1)
            res *= a;
    return res;
}

2、补一下取余的公式

(a+b)%c=((a%c)+(b%c))%c                             防止溢出
( a − b ) % c = ( ( a % c ) − b + c ) % c           加c, 防止出现负数
( a × b ) % c = ( ( a % c ) × ( b % c ) ) % c       防止溢出

3、关于分式的取余

今天遇到的问题是:如果c并不是质数,那么就不好用上面两种方法(如:上帝造裸题的七分钟)。

4、矩阵快速幂 & 斐波那契数列

今天遇到的这题是:快速求解斐波那契数列的前n项。当然在时间空间足够的情况下,直接上数组就行。但是在比赛中,时间有限,而空间也仅支撑到106次方,对于动不动1018的测试点真的伤不起。

解法:矩阵快速幂。(所以在这里提是因为:明明之前都写过了,就下面例题里面那个,但是太久了,而且看快速幂的时候也懒得再看一遍例题,so,在这里强调一下)

点1:

//简而言之就是,斐波那契数列的 前n项和 = f(n+2)-1
//这一步,将求n项和,转化为求某一项
ans
= [f(1)+f(3)+f(5)+⋯+f(n)]  +  [f(2)+f(4)+f(6)+⋯+f(n−1)]
= [f(2)+f(3)+f(5)+⋯+f(n)] + [f(1)+f(2)+f(4)+f(6)+⋯+f(n−1)]−f(1)
= f(n+1)+f(n)−f(1)
= f(n+2)−1

点2:

//求斐波那契数列的第n项和,用矩阵快速幂的方法,可以大大节省时间。
//代码在下面例题,这里不重复。

矩阵快速幂:

  • 定义
  • 函数:矩阵乘法
  • 函数:快速幂
  • 主函数

定义:

//矩阵定义
struct node {
	int mat[15][15];
}x, y;

矩阵乘法:

//矩阵乘法
node multi(node x, node y) {
	node tmp;
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < len; j++) {
			tmp.mat[i][j] = 0;
			for (int k = 0; k < len; k++) {
				tmp.mat[i][j] += (x.mat[i][k] * y.mat[k][j]) % mod;
			}
			tmp.mat[i][j] = tmp.mat[i][j] % mod;
		}
	}
	return tmp;
}

快速幂:

//矩阵快速幂
node matpow(node x, node y, int num) {
	while (num) {
		if (num & 1)
		{
			y = multi(y, x);
		}
		x = multi(x, x);
		num = num >> 1;
	}
	return y;
}

例1:矩阵快速幂

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define mod  9973

//矩阵定义,结构体
struct node
{
    ll ma[11][11];
};
int n, k;

//矩阵乘法
node mul(node a, node b)
{
    node ans;
    memset(ans.ma, 0, sizeof(ans.ma));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= n; k++)
                ans.ma[i][j] = (ans.ma[i][j] + a.ma[i][k] * b.ma[k][j]) % mod;
    return ans;
}

//快速幂
node pow(node a, int pow)
{
    //初始化ans矩阵为单位矩阵
    node ans;
    memset(ans.ma, 0, sizeof ans.ma);
    for (int i = 1; i <= n; i++)
    {
        ans.ma[i][i] = 1;
    }
    //当pow的二进制位为1,ans就乘a。
    //a每次都乘a
    while (pow)
    {
        if (pow & 1)
        {
            ans = mul(ans, a);
        }
        a = mul(a, a);
        pow >>= 1;
    }
    return ans;
}

int main()
{
    int temp;
    int t;
    cin >> t;
    while (t--)
    {
        scanf("%d %d", &n, &k);
        node a;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
            {
                scanf("%d", &temp);
                a.ma[i][j] = temp;
            }
        node ans = pow(a, k);
        ll sum = 0;
        for (int i = 1; i <= n; i++) sum = (sum + ans.ma[i][i]) % mod;
        cout << sum << endl;
    }
}

例2:矩阵快速幂+递推(斐波那契)

其实跟例1大同小异,放上来是因为斐波那契这种矩阵解法很不错,比递归快多了

In the Fibonacci integer sequence,F0= 0,F1= 1, andFn=Fn− 1+Fn− 2forn≥ 2. For example, the first ten terms of the Fibonacci sequence are: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, … An alternative formula for the Fibonacci sequence is 在这里插入图片描述 Given an integern, your goal is to compute the last 4 digits ofFn.
Sample Input
0
9
999999999
1000000000
-1
Sample Output
0
34
626
6875
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod 10000
#define ll long long
using namespace std;

//矩阵定义,结构体
struct mat
{
    ll m[11][11];
    mat()
    {
        memset(m, 0, sizeof(m));
    }
};
ll n = 2, k;
//矩阵乘法
mat mul(mat a, mat b)
{
    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] = (ans.m[i][j] + a.m[i][k] * b.m[k][j]) % mod;
    return ans;
}
//快速幂
mat pow(mat a, int pow)
{
    //初始化ans矩阵为单位矩阵
    mat ans;
    for (int i = 1; i <= n; i++)
    {
        ans.m[i][i] = 1;
    }
    //当pow的二进制位为1,ans就乘a。
    //a每次都乘a
    while (pow)
    {
        if (pow & 1)
        {
            ans = mul(ans, a);
        }
        a = mul(a, a);
        pow >>= 1;
    }
    return ans;
}

int main()
{
    int x;
    while (scanf("%d", &x) != EOF)
    {
        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(a, x);
        if (x)
        {
            printf("%lld\n", a.m[1][2]);
        }
        else
        {
            printf("0\n");
        }
    }
}

例3:矩阵快速幂+递推

Dwarfs种了一株非常有意思的植物,这株植物像一个方向向上的三角形。它有一个迷人的特点,那就是在一年后一株方向向上的三角形的植物就会被分成4株三角形的植物:它们当中的三株方向是向上的,一株方向是向下的。 又一年之后,每株植物都会分成四个,规则如上。之后的每年都会重复这一过程。下面的图说明了这一发展过程。 请帮助Dwarfs算出n年后将会有多少个方向向上的三角形。

Input
第一行包括一个整数n(0<=n<=101810^{18}1018),即这植株生长的总年份。 ps:c++选手请不要用%lld来读取或输出long long类型,cin/cout或%I64d更好一些。
0
1
2
3
Output
输出一个整数,即n年后向上的三角形的个数(答案对1000000007(10910^{9}109+7)取模)
1
3
10
36

解:
没给出递推式,但可以推出

#include <cstdio>
#include <iostream>
#define ll long long
#define n 2
#define mod 1000000007;
using namespace std;
struct mat
{
	ll m[2][2];
	mat()
	{
		memset(m, 0, sizeof(m));
	}
};
mat multi(mat a, mat b)
{
	int i, j, k;
	mat result;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < n; j++)
		{
			for (k = 0; k < n; k++)
			{
				result.m[i][j] += (a.m[i][k] * b.m[k][j]);
				result.m[i][j] %= mod;
			}
		}
	}
	return result;
}
mat powfirst(mat a, int pow)
{
	mat result;
	result.m[0][0] = result.m[1][1] = 1;
	while (pow)
	{
		if (pow & 1)		result = multi(result, a);
		a = multi(a, a);
		pow = pow >> 1;
	}
	return result;
}
int main()
{
	int x;
	mat a;
	mat result;
	while (scanf("%d", &x) != EOF)
	{
		result.m[0][0] = result.m[1][0] = 1;
		a.m[0][0] = 4;
		a.m[0][1] = -1;
		a.m[1][0] = 0;
		a.m[1][1] = 2;
		result = multi(powfirst(a, x), result);
		cout << result.m[0][0] << endl;
	}
}

例4:矩阵快速幂+递推

(sqrt(2)+sqrt(3))^2n %1024向下取整的值

总结

几个易错的点:

  • 阶数n和mod记得在宏定义
  • 矩阵a要在while里面初始化
  • 一般是aaaab,不是baa*a。

刚开始想简化一下,就想这样写

powfirst函数
{
	result初始化为b而非单位矩阵;//该result是矩阵连乘的第一个矩阵,与主函数中result不是同一个意思
	&hellip;&hellip;
}
主函数
{
	定义result函数;
	result = powfirst(a,pow);
    输出result的某一位;
}

但是这样就baa*a了,所以应该是

powfirst函数
{
	result初始化为单位矩阵;//该result是矩阵连乘的第一个矩阵,与主函数中result不是同一个意思
	&hellip;&hellip;
}
主函数
{
	定义result并初始化为b;
	a = powfirst(a,pow);
	result = multi(a,result);
	cout result的某一位;
}

参考:

https://blog.csdn.net/red_red_red/article/details/90208713?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163600994116780366591052%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163600994116780366591052&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-1-90208713.pc_search_result_control_group&utm_term=%E7%9F%A9%E9%98%B5%E5%BF%AB%E9%80%9F%E5%B9%82%E7%9A%84%E9%A2%98%E7%9B%AE&spm=1018.2226.3001.4187

例题:

https://blog.csdn.net/boliu147258/article/details/105050787?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163600994016780255276632%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163600994016780255276632&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-3-105050787.pc_search_result_control_group&utm_term=%E7%9F%A9%E9%98%B5%E5%BF%AB%E9%80%9F%E5%B9%82%E7%9A%84%E9%A2%98%E7%9B%AE&spm=1018.2226.3001.4187

https://blog.csdn.net/weixin_44059127/article/details/104301609?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%A9%E9%98%B5%E5%BF%AB%E9%80%9F%E5%B9%82%E7%9A%84%E9%A2%98%E7%9B%AE&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-104301609.pc_search_result_control_group&spm=1018.2226.3001.4187

https://blog.csdn.net/winycg/article/details/69218723?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%A9%E9%98%B5%E5%BF%AB%E9%80%9F%E5%B9%82%E7%9A%84%E9%A2%98%E7%9B%AE&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-69218723.pc_search_result_control_group&spm=1018.2226.3001.4187

https://winycg.blog.csdn.net/article/details/69218723

https://blog.csdn.net/red_red_red/article/details/90208713

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值