1. 程式人生 > >快速乘 快速冪 矩陣快速冪

快速乘 快速冪 矩陣快速冪

求pow(a, exp)% mod 的值,快速冪其實也是利用了倍增的思想在裡面,比如求2^12 = 2^6 * 2^6 = 2^3 * 2^3 * 2^3 * 2^3 = 2 * 2^2 * 2 * 2^2 * 2 * 2^2 * 2 * 2^2 = 2 * 2 * 2 

* 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;即求2^12 可以用2^6 * 2^6 再進一步用之前求得的,這裡只要區分指數為奇數和偶數區別對待即可。

typedef unsigned long long ll;
const ll mod = 100000000007;

ll quick_pow(ll a, ll exp, ll mod)  //mod比較大,用int或者long long下面的相乘會溢位
{
        ll ans = 1;
        a %= mod;
        while(exp)
        {
                if(exp & 1)         //指數為奇數
                {
                        ans = ((ans % mod) * (a % mod)) % mod;
                        exp--;
                }
                exp >>= 1;
                a = ((a % mod) * (a % mod)) % mod;
        }
        return ans;
}

矩陣快速冪和上述快速冪其實是一個原理,只不過這裡我們定義一個矩陣的結構體,方便操作:

const int MOD = 1e9+7;
const int MAXN=105;
typedef long long ll;
int n;
struct Matrix
{
    ll mat[MAXN][MAXN];
    friend Matrix operator*(Matrix &m1, Matrix &m2) {
        Matrix pro;
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < n; ++j) {
                ll tmp = 0;
                for(int k = 0; k < n; ++k) {
                    tmp = (tmp + m1.mat[i][k]*m2.mat[k][j] % MOD) % MOD;
                }
                pro.mat[i][j] = tmp;
            }
        }
        return pro;
    }
};


Matrix quickPow(Matrix m, int p)
{
    Matrix ans;
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < n; ++j) {
            ans.mat[i][j] = 0;
        }
        // ans.mat[i][i] = 1;  無向圖關係矩陣M可通過 M^k 求 u 到 v 路徑長度為k總方案數 (M^k).mat[u][v]
    }
    while(p) {
        if(p & 1) ans = ans*m;
        m = m*m;
        p >>= 1;
    }
    return ans;
}

快速乘:a * k  相當於 k個a相加,處理過程和快速冪異曲同工,只是比如求 (a*b) % p時,a*b可能很大,用快速乘可以在“相乘”的過程中取模而不影響結果,防止溢位。

需要注意的是,k必須為非負整數;

typedef long long ll;
ll quickMul(ll a, ll k, ll m) {    // a * k 相當於k個a相加   1 2 4 ....
    ll ret = 0;
    while(k){
        if(k & 1) ret = (ret + a) % m;
        k >>= 1;
        a = (a << 1) % m;
    }
    return ret;
}