C++【矩阵加速】POJ3233 Matrix Power Series

该博客介绍了如何利用矩阵加速技术解决POJ3233问题,通过矩阵乘法简化求解Sk=∑i=1kximodm的过程。难点包括矩阵套矩阵的操作及单位矩阵和零矩阵的应用。


题目描述

POJ-3233
看到这令人目瞪口呆的数据范围, O ( log ⁡ n ) O(\log n) O(logn)没逃了,于是,最好想到的肯定是矩阵加速。

前置芝士

矩阵,矩阵乘法矩阵加速

到这里找就好了嘛

问题的简化

首先抛掉矩阵,先想一想这样一道题:
给定 m , k , x m,k,x m,k,x,求 S k = ∑ i = 1 k x i   m o d   m S_k=\sum_{i=1}^{k}x^i\bmod m Sk=i=1kximodm

这道题可以轻松想出两种对于矩阵加速状态的定义: [ S i x i ] \begin{bmatrix}S_i&x^i\end{bmatrix} [Sixi] [ S i 1 ] \begin{bmatrix}S_i&1\end{bmatrix} [Si1]
对于两种状态,都很容易构造出的加速矩阵,如下:
[ 1 0 x x ] a n d [ x 0 x 1 ] \begin{bmatrix}1&0\\x&x\end{bmatrix}\qquad and\qquad\begin{bmatrix}x&0\\x&1\end{bmatrix} [1x0x]and[xx01]
于是这道题就AC了……

本题做法

就和上面的题一样,就是把原先的 x x x变成了矩阵 A A A

难点1

原先的 x x x是要放在一个矩阵中的,那把矩阵 A A A还要套在矩阵中吗?怎么套?
这是可以实现的,使用矩阵套矩阵的方法,模板代码如下:

int N, M;
struct Matrix{
    int n, m;
    long long A[31][31];
    inline Matrix(int N = 0, int M = 0) {
        n = N; m = M; memset(A, 0, sizeof A);
    }
    inline Matrix operator * (const Matrix &rhs) const {
        Matrix ans = Matrix(n, rhs.m);
        for(reg int k = 1; k <= m; k++)
            for(reg int i = 1; i <= ans.n; i++)
                for(reg int j = 1; j <= ans.m; j++)//据说kij比ijk快(Floyd?)
                    ans.A[i][j] = ans.A[i][j] + A[i][k] * rhs.A[k][j];
        return ans;
    }
    inline Matrix operator + (const Matrix & rhs) const {
        Matrix ans = Matrix(n, m);
        for(reg int i = 1; i <= n; i++)
            for(reg int j = 1; j <= m; j++)
                ans.A[i][j] = (A[i][j] + rhs.A[i][j]) % MOD;
        return ans;
    }
};
struct Mmatrix{
    int n, m;
    Matrix A[3][3];
    inline Mmatrix(int N = 0, int M = 0) {
        n = N; m = M; memset(A, 0, sizeof A);
    }
    inline Mmatrix operator * (const Mmatrix& rhs) const {
        Mmatrix ans = Mmatrix(n, rhs.m);
        for(int k = 1; k <= m; k++)
            for(int i = 1; i <= ans.n; i++)
                for(int j = 1; j <= ans.m; j++) {
                    if(!ans.A[i][j].n) ans.A[i][j] = Matrix(N, M);
                    //注意这一步很关键,因为这个时候ans.A[i][j]还是一个0行0列的矩阵
                    //需要把它赋上行和列的值,才能进行加法运算,否则答案就会为0
                    ans.A[i][j] = ans.A[i][j] + A[i][k] * rhs.A[k][j];
                }
        return ans;
    }
};
难点2

原先定义的矩阵中是有一个 1 1 1 0 0 0的,但是到了矩阵中 1 1 1 0 0 0要变成什么呢?

因为矩阵加速用的是乘法运算,所以令表示 1 1 1的矩阵为 E E E,则 E E E要做到的就是 E × A E\times A E×A A A A为任意矩阵) = A =A =A
所以可以轻松的得到 E E E是一个对角线的矩阵,又称单位矩阵。
至于 0 0 0的代替物,就是全为 0 0 0的矩阵辣!

代码(你看不到我)

方法1:自动省略input和output

int main() {
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin >> N >> k >> MOD;
    A.input(N, N);
    E = Matrix(N, N);
    for(reg int i = 1; i <= N; i++) E.A[i][i] = 1;
    O = Matrix(N, N);
 
    f = Mmatrix(1, 2);//S_1, A^1
    f.A[1][1] = A; f.A[1][2] = A;
    h = Mmatrix(2, 2);
    h.A[1][1] = A; h.A[1][2] = O;
    h.A[2][1] = E; h.A[2][2] = E;
 
    piow(k - 1);
    f.A[1][1].output();
}

方法2:

int main() {
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin >> N >> k >> MOD;
    A.input(N, N);
    E = Matrix(N, N);
    for(reg int i = 1; i <= N; i++) E.A[i][i] = 1;
    O = Matrix(N, N);

    f = Mmatrix(1, 2);
    f.A[1][1] = A; f.A[1][2] = E;
    h = Mmatrix(2, 2);
    h.A[1][1] = A; h.A[1][2] = O;
    h.A[2][1] = A; h.A[2][2] = E;

    piow(k - 1);
    f.A[1][1].output();
}

附:头文件集锦

不打这么多头文件我看不起你!!!

#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<ctime>
#include<vector>
#include<bitset>
#include<cctype>
#include<string>
#include<sstream>
#include<fstream>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<tr1/unordered_map>
#include<tr1/unordered_set>
using namespace std;
using namespace tr1;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值