【题解】P10664 BZOJ3328 PYXFIB

【模板】单位根反演(其实不太模板

单位根反演的关键结论是:\(n[n\mid k]=\sum\limits_{i=0}^{n-1}{\omega_{n}^{ik}}\),其中 \(\omega_{n}\) 是在模意义下的单位根。具体来说有 \(\omega\equiv g^{\frac{p-1}{n}}\pmod p\),其中 \(p\) 是模数,\(g\)\(p\) 的一个原根。

然后考虑用上面的结论来推这个题:

\[\begin{aligned} &\sum\limits_{i=0}^n[k\mid i]\binom niF_i\\ =&\frac1k\sum\limits_{i=0}^n(\sum\limits_{j=0}^{k-1}\omega^{ij})\binom niF_i\\ =&\frac1k\sum\limits_{j=0}^{k-1}\sum\limits_{i=0}^n\omega^{ij}\binom niF_i\\ =&\frac1k\sum\limits_{j=0}^{k-1}\sum\limits_{i=0}^n(\omega^j)^i\binom niF_i\\ \end{aligned} \]

套路的把 \(F\) 即斐波那契数列用矩阵的形式表示出来:

\[\begin{aligned} &\frac1k\sum\limits_{j=0}^{k-1}\sum\limits_{i=0}^n(\omega^j)^i\binom niF_i\\ =&\frac 1k\sum\limits_{j=0}^{k-1}\sum\limits_{i=0}^n\binom ni(\begin{bmatrix}&1&1&\\&1&0&\\\end{bmatrix}\omega^j)^i\\ =&\frac 1k\sum\limits_{j=0}^{k-1}\sum\limits_{i=0}^n\binom ni(\textbf{I}^{n-i})(\begin{bmatrix}&1&1&\\&1&0&\\\end{bmatrix}\omega^j)^i\\ =&\frac 1k\sum\limits_{j=0}^{k-1}(\textbf{I}+\begin{bmatrix}&1&1&\\&1&0&\\\end{bmatrix}\omega^j)^n\\ \end{aligned} \]

\(\omega=g^{\frac{p-1}n}\) 带入原式,用矩阵快速幂优化上述式子的计算,除去找原根 \(g\) 所需的时间复杂度,总时间复杂度为 \(O(Tk\log n\omega^3)\),其中在这里 \(\omega\) 定义为矩阵的大小,本题中取 \(\omega=2\)

实现起来稍微有点卡常,一个比较好的卡常方法是展开矩阵乘法:

// Author: 美丽好 rua 的大宋宋
// #pragma GCC optimize(3, "Ofast", "inline", "unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#define int long long
using namespace std;
using i64 = long long;
const int N = 1000010;
const int inf = 1e18;
const int mod = 998244353;

inline int power(int a, int b, int c)
{
    int ans = 1;
    while (b)
    {
        if (b & 1)
            ans = 1ll * ans * a % c;
        a = 1ll * a * a % c, b >>= 1;
    }
    return ans;
}
inline int inversion(int x) { return power(x, mod - 2, mod); }

using i128 = __int128;
inline void chkmin(int &x, int y)
{
    if (x > y)
        x = y;
}
inline void chkmax(int &x, int y)
{
    if (x < y)
        x = y;
}

vector<int> fj;
inline int chk(int x, int p)
{
    for (int &i : fj)
        if (power(x, (p - 1) / i, p) == 1)
            return 0;
    return 1;
}

inline int find_g(int p)
{
    for (int i = 2;; ++i)
        if (chk(i, p))
            return i;
    return -1;
}

int n, k, p, g, omega;

struct Matrix
{
    int a[2][2];
    inline Matrix() { memset(a, 0, sizeof a); }
} ;
inline Matrix operator*(const Matrix &l, const Matrix &r)
{
    Matrix o;
    o.a[0][0] = (l.a[0][0] * r.a[0][0] + l.a[0][1] * r.a[1][0]) % p;
    o.a[0][1] = (l.a[0][0] * r.a[0][1] + l.a[0][1] * r.a[1][1]) % p;
    o.a[1][0] = (l.a[1][0] * r.a[0][0] + l.a[1][1] * r.a[1][0]) % p;
    o.a[1][1] = (l.a[1][0] * r.a[0][1] + l.a[1][1] * r.a[1][1]) % p;
    return o;
}
const inline Matrix I()
{
    Matrix o;
    o.a[0][0] = o.a[1][1] = 1;
    o.a[1][0] = o.a[0][1] = 0;
    return o;
}
inline Matrix power(Matrix a, int b)
{
    if (!b)
        return I();
    auto o = power(a, b >> 1);
    o = o * o;
    if (b & 1)
        o = o * a;
    return o;
}

signed main()
{
    cin.tie(0)->sync_with_stdio(false);
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n >> k >> p;
        fj.clear();
        int _ = p - 1;
        for (int i = 2; i * i <= _; ++i)
            if (_ % i == 0)
            {
                fj.emplace_back(i);
                while (_ % i == 0)
                    _ /= i;
            }
        if (_ > 1)
            fj.emplace_back(_);
        g = find_g(p);
        omega = power(g, (p - 1) / k, p);
        const int inv_k = power(k % p, p - 2, p);
        const auto mat_i = I();
        Matrix mat_base;
        mat_base.a[0][0] = mat_base.a[0][1] = mat_base.a[1][0] = 1;
        mat_base.a[1][1] = 0;
        // cerr << "wa " << g << ' ' << omega << '\n';
        int omega_pwr = 1, sum = 0;
        for (int j = 0; j < k; ++j)
        {
            Matrix mat_inner;
            for (int x = 0; x < 2; ++x)
                for (int y = 0; y < 2; ++y)
                    mat_inner.a[x][y] = (mat_base.a[x][y] * omega_pwr + mat_i.a[x][y]) % p;
            Matrix mat_pwr = power(mat_inner, n);
            sum = (sum + mat_pwr.a[0][0]) % p;
            // cerr << j << ": " << mat_inner.a[0][0] << '\n';
            omega_pwr = omega_pwr * omega % p;
            // cerr << j << ": " << mat_pwr.a[0][0] << '\n';
        }
        sum = sum * inv_k % p;
        cout << sum << '\n';
    }
    return 0;
}

/*

1
10 9 37

*/

/*

33

*/

posted @ 2026-01-31 19:16  0103abc  阅读(3)  评论(0)    收藏  举报