矩阵优化

  前几天武理的新生赛考了道矩阵优化的斐波那契,感觉忘得差不多了

 

前置知识

  • 矩阵乘法

  一个 n * p 的矩阵 A 和一个 p * m 的矩阵 B 相乘得到 n * m 的矩阵 C ,其中 C[i][j] = ∑A[i][k] * B[k][j] ; (k = 1, 2, 3... n)

  • 矩阵快速幂

  与普通快速幂相似,初始化cnt, x,再重载一下运算符,即可直接套ksm的板子

node ksm(ll y)
{
    node cnt, x; 
    初始化cnt, x;
    while(y)
    {
        if(y & 1) cnt = cnt * x;
        x = x * x; y >>= 1;
    }
    return cnt;
}
伪代码

 

矩阵优化

  • 适用范围
  1.  n 非常大,通常在long long
  2.  转移决策较少,系数为常数
  3.  通常会对一个数取模

  矩阵优化能在 O(logn) 的时间内算出 f[n] 的值

  • 步骤

  以斐波那契数列为例

  1. 计算递推式

  f[n] = f[n - 1] + f[n - 2];

  2. 构造矩阵

  我们将 {f[i], f[i - 1]} 视为一个 1 * 2 的矩阵,需要乘上一个 2 * 2 的矩阵使其转化成 {f[i], f[i - 1]},即向后递推一位

  通过计算可得,这个 2 * 2 的矩阵 x 为 

 

  3. 矩阵快速幂

  通过矩阵进行递推,将原本只需一步的转移变成多次加乘,看起来更加复杂了。但是,矩阵的转移可以用上快速幂,n 次转移即相当于  {f[i], f[i - 1]} * x ^ n

  • 模板

  luogu1962 斐波那契数列

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1000000007;

struct node
{
    ll dp[3][3];
    void clear() {memset(dp, 0, sizeof(dp));}
}f, ans;

node operator * (node x, node y)
{
    node cnt; cnt.clear();
    for(int i = 1; i <= 2; i++)
        for(int j = 1; j <= 2; j++)
            for(int k = 1; k <= 2; k++)
                cnt.dp[i][j] = (cnt.dp[i][j] + x.dp[i][k] * y.dp[k][j] % mod) % mod;
    return cnt;
}

node ksm(ll y)
{
    node cnt, x; cnt.clear(); x.clear();
    cnt.dp[1][1] = cnt.dp[1][2] = 1;
    x.dp[1][1] = x.dp[1][2] = x.dp[2][1] = 1;
    while(y)
    {
        if(y & 1) cnt = cnt * x;
        x = x * x; y >>= 1;
    }
    return cnt;
}

int main()
{
    ll n; cin >> n;
    if(n <= 2) {cout << 1; return 0;}
    f.dp[1][1] = f.dp[1][2] = 1;
    node ans = ksm(n - 2);//{f[2], f[1]}经过n - 2次转移得到{f[n], f[n - 1]}
    cout << ans.dp[1][1];
    return 0;
}
luogu1962

 

posted @ 2019-11-20 13:02  XYZinc  阅读(517)  评论(0编辑  收藏  举报