「题解」洛谷 P2044 [NOI2012]随机数生成器

题目

P2044 [NOI2012]随机数生成器

简化题意

\(\large x_{i + 1} = (ax_i + c) \mod m\) 的第 \(n\) 项。

\(\large 1 \leq n \leq 10 ^ {18}\)

思路

矩阵乘法。

递推用的矩阵也很好求。

\(\large \left[\begin{array}{ccc}x_i & c\end{array}\right] \times \left[\begin{array}{ccc}a & 1 \\ 1 & 1\end{array}\right] = \left[\begin{array}{ccc}x_{i + 1} & c\end{array}\right]\)

小心模数很大,乘起来会爆long long所以要用龟速乘。

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>

typedef long long ll;
ll m, a, c, x0, n, g;

ll qmul(ll x, ll y) {
    ll ans = 0;
    while (y) {
        if (y & 1) ans = ans + x, ans %= m;
        x = (x + x) % m;
        y >>= 1;
    }
    return ans % m;
}

struct Matrix {
    ll jz[4][4];
    Matrix() { memset(jz, 0, sizeof jz); }
    void one() {
        for (int i = 1; i <= 4; ++i) jz[i][i] = 1;
    }
    friend Matrix operator * (Matrix a, Matrix b) {
        Matrix c;
        for (int k = 1; k <= 2; ++k) {
            for (int i = 1; i <= 2; ++i) {
                for (int j = 1; j <= 2; ++j) {
                    c.jz[i][j] = (c.jz[i][j] + qmul(a.jz[i][k], b.jz[k][j])) % m;
                }
            }
        }
        return c;
    }
};

Matrix qpow(Matrix a, ll b) {
    Matrix ans, base = a;
    ans.one();
    while (b) {
        if (b & 1) ans = ans * base;
        base = base * base;
        b >>= 1;
    }
    return ans;
}

int main() {
    scanf("%lld %lld %lld %lld %lld %lld", &m ,&a, &c, &x0, &n, &g);
    Matrix b, nor;
    b.jz[1][1] = a % m, b.jz[1][2] = 0, b.jz[2][1] = 1, b.jz[2][2] = 1;
    nor.jz[1][1] = x0 % m, nor.jz[1][2] = c % m;
    b = qpow(b, n);
    Matrix ans;
    for (int k = 1; k <= 2; ++k) {
        for (int i = 1; i <= 1; ++i) {
            for (int j = 1; j <= 2; ++j) {
                ans.jz[i][j] = (ans.jz[i][j] + qmul(nor.jz[i][k], b.jz[k][j])) % m;
            }
        }
    }
    std::cout << ans.jz[1][1] % g<< '\n';
    return 0;
}
posted @ 2020-09-02 22:17  yu__xuan  阅读(187)  评论(0编辑  收藏  举报