矩阵快速幂//洛谷P3390

第一行——咕咕咕。

第二行——啊啊啊啊啊啊啊我好饿啊好饿啊好饿啊好饿啊。饿到灵魂出窍。

快速幂:

首先了解一下取模规则:

  1. (a + b) % p = (a % p + b % p) % p

  2. (a - b) % p = (a % p - b % p ) % p 

  3. (a * b) % p = (a % p * b % p) % p 

快速幂的思想为:每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。所以在此基础上可以实现幂次以指数级降低,最终为logn

ex:

  1. 2^10 = 2^5 * 2^5
  2. 2^5 = 2 * 2^4
  3. 2^4 = 2^2 * 2^2
  4. 2^2 = 2^1 * 2^1
  5. 2^1 = 2 * 2^0

1)当b是奇数时,那么有 a^b = a * a^*(b-1)

2)当b是偶数时,那么有 a^b = a^(b/2) * a^(b/2)

将指数写成2进制的形式,可以发现二进制的i号位为1,那么a^(2^i)就被选中。于是可以得到计算a^b的大致思路:令i 从0到k枚举b的二进制的每一位,如果为1那就累积a^(2^i)。ex:10 = 1010,则2^10 = 2^(2^3)*2^(2^1)。

ll quick(ll a, ll b, ll p = mod)
{
    a = (a % p + p) % p; //当 a 一定在 [0, p - 1] 之间时,此行不必要
    ll s = 1;//用s来存储累积的结果
    while(b)
    {
    if(b & 1) s = s * a % p;//如果b为奇数,s*a
    a = a * a % p;//a平方
    b >>= 1;//b/2
    }
    return s % p;
}

矩阵快速幂其实就是在矩阵乘法的基础上运用了快速幂,不做过多解释。只需要注意矩阵的乘法是行元素列元素对应相乘相加生成新的矩阵即可。

应用:

利用矩阵快速幂求Fibonacci POJ3070,需要记住公式(一般会给的叭

公式

本代码以洛谷P3390为例

先附题目链接洛谷P3390

这题是裸的模板题,解释都在代码里

我用结构体存的,其实我觉得直接用数组就很方便了

下附代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define ll long long
#define maxn 0x3f3f3f3f
#define mod 1000000007
using namespace std;

int n;

struct matrix
{
    ll a[105][105];
} m,dw; //用于存矩阵,m表示原矩阵,dw表示单位矩阵

matrix mul(matrix A,matrix B)
{
    matrix temp;//存过程中的m1*m2的矩阵
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            temp.a[i][j] = 0;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            for(int k = 1; k <= n; ++k)
            {
                temp.a[i][j] += (A.a[i][k]%mod)*(B.a[k][j]%mod)%mod;
                temp.a[i][j] %= mod;
            }
    return temp;
}//用于计算n阶矩阵相乘的结果(线代中矩阵的乘法运算法则

void quik_pow(ll k)//n阶矩阵的k次幂
{
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
        {
            if(i == j)
            dw.a[i][j] = 1;
        }
    while(k != 0)
    {
        if(k%2 == 1)
            dw = mul(dw,m);
        m = mul(m,m);
        k /= 2;
    }
}//类似于数的快速幂,换成矩阵即可
int main()
{
    ll k;
    scanf("%d%lld",&n,&k);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
            scanf("%lld",&m.a[i][j]);
    quik_pow(k);
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= n; ++j)
        {
            if(j == 1)
                printf("%lld",dw.a[i][j]%mod);
            else
                printf("% lld",dw.a[i][j]%mod);
        }
        printf("\n");
    }
    return 0;
}

不要问我为啥取模奇奇怪怪的,为了保险我哪儿哪儿都取了,上一次提交爆了,干脆都取模嘻嘻

诚恳提问:当我令mod = 1000000000+7的时候结果就跟mod = 1000000007不一样的原因是啥鸭,十分方张。

欢迎指出错误qwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值