洛谷4000 斐波那契数列

$$ \text{求}F\left( n \right) \,\,mod\,\,p,n\le 10^{30000000},p\le 2^{31}-1 \\ \text{斐波那契数列在模}p\text{意义下的循环节长度为}O\left( p \right) \text{级别。} \\ \text{首先将}p\text{进行质因数分解,得到}p_{1}^{k_1}p_{2}^{k_2}p_{3}^{k_3}… \\ \text{设}T\left( k \right) \text{为模}k\text{意义下的循环节长度,则}T\left( p \right) =\prod{T\left( p_i^{k_i} \right)} \\ \text{同时对于}p^k,\text{有}T\left( p^k \right) =T\left( p \right) \times p^{k-1} \\ \text{考虑如何求出模质数的循环节长度。} \\ \text{对于2,3,5直接特判。} \\ \text{对于其他质数}p\text{,如果5是模}p\text{意义下的二次剩余}\left( \text{即}5^{\frac{p-1}{2}}\equiv 1\left( mod\,\,p \right) \right) ,\text{则模}p\text{意义下的循环节长度是}p-\text{1的约数。} \\ \text{部分证明:} \\ \text{斐波那契数列的通项公式为}F_n=\frac{\sqrt{5}}{5}\left( \left( \frac{\left( 1+\sqrt{5} \right)}{2} \right) ^n-\left( \frac{\left( 1-\sqrt{5} \right)}{2} \right) ^n \right) \\ \text{设5在模}p\text{意义下的二次剩余为}x\text{,则}F_n\text{一定可以表示为}k\left( x_0^n-x_1^n \right) \text{的形式。} \\ \text{其中}x_0^n\text{和}x_1^n\text{的周期一定是}\varphi \left( p \right) \text{的约数。} \\ \text{故}F_n\text{的周期是}\varphi \left( p \right) \text{的约数。} \\ \\ \text{反之,模}p\text{意义下的循环节长度是}2\left( p+1 \right) \text{的约数。} \\ \text{枚举约数验证即可。} \\ \text{这个我不会证} \\ \\ $$

#include <climits>
#include <cstring>
#include <iostream>
#include <map>
using int_t = long long int;
using std::cin;
using std::cout;
using std::endl;
struct Matrix mat_mul(const struct Matrix& a, const struct Matrix& b,
                      int_t mod);
int_t power(int_t base, int_t index, int_t mod);
struct Matrix {
    int_t data[4][4];
    Matrix() { memset(data, 0, sizeof(data)); }
    int_t& operator()(int_t r, int_t c) { return data[r][c]; }
    const int_t N = 2;
    Matrix& operator=(const Matrix& mat2) {
        memcpy(data, mat2.data, sizeof(data));
        return *this;
    }
    Matrix power(int_t index, int_t mod) {
        Matrix result;
        Matrix base = *this;
        result(1, 1) = result(2, 2) = 1;
        while (index) {
            if (index & 1) result = mat_mul(result, base, mod);
            base = mat_mul(base, base, mod);
            index >>= 1;
        }
        return result;
    }
};

Matrix mat_mul(const Matrix& a, const Matrix& b, int_t mod) {
    const auto N = a.N;
    Matrix result;
    for (int_t i = 1; i <= N; i++) {
        for (int_t j = 1; j <= N; j++) {
            for (int_t k = 1; k <= N; k++) {
                result.data[i][j] =
                    (result.data[i][j] + a.data[i][k] * b.data[k][j] % mod) %
                    mod;
            }
        }
    }
    return result;
}
Matrix F(int_t k, int_t mod) {
    Matrix mat;
    mat(1, 1) = 0;
    mat(1, 2) = 1;
    mat(2, 1) = 1;
    mat(2, 2) = 1;
    return mat.power(k, mod);
}
//计算模p意义下的循环周期,p是质数
int_t getPeriod(int_t p) {
    if (p == 2)
        return 3;
    else if (p == 3)
        return 8;
    else if (p == 5)
        return 20;
    int_t fac;
    if (power(5, (p - 1) / 2, p) == 1) {
        fac = p - 1;
    } else {
        fac = 2 * (p + 1);
    }
    const auto check = [&](int_t x) -> bool {
        Matrix res = ::F(x + 1, p);
        return res(1, 1) == 0 && res(1, 2) == 1;
    };
    int_t result = 1e12;
    for (int_t i = 1; i * i <= fac; i++) {
        if (fac % i == 0) {
            if (i != 1 && check(i)) {
                result = std::min(result, i);
            }
            if (fac / i != i && check(fac / i)) {
                result = std::min(result, fac / i);
            }
        }
    }
    return result;
}
std::map<int_t, int_t> divide(int_t x) {
    std::map<int_t, int_t> result;
    for (int_t i = 2; i * i <= x; i++) {
        if (x % i == 0) {
            result[i] = 0;
            while (x % i == 0) {
                x /= i;
                result[i]++;
            }
        }
    }
    if (x > 1) result[x] += 1;
    return result;
}
int main() {
    static char buf[30000000 + 10];
    scanf("%s", buf);
    int_t p;
    cin >> p;
    int_t length = 1;
    for (const auto& pair : divide(p)) {
        length *= ::getPeriod(pair.first) *
                  power(pair.first, pair.second - 1, int_t(1) << 40);
    }
    int_t sum = 0;
    for (auto ptr = buf; *ptr != '\0'; ptr++) {
        sum = (sum * 10 + *ptr - '0') % length;
    }
    cout << F(sum , p)(1, 2) << endl;
    return 0;
}

int_t power(int_t base, int_t index, int_t mod) {
    int_t result = 1;
    while (index) {
        if (index & 1) result = result * base % mod;
        index >>= 1;
        base = base * base % mod;
    }
    return result;
}

 

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理