bzoj 3328: PYXFIB

题目传送门:bzoj 3328

题意简述:

题目说的很清楚了。

题解:

首先注意到:

\[\mathrm{Ans}=\sum_{i}\binom{n}{i}F_{i}[k|i]\]

考虑矩阵 \(\mathbf{A}=\begin{bmatrix}1&1\\1&0\end{bmatrix}\),则 \(F_i=\left[A^i\right]_{1,1}\)

所以有:

\[\mathrm{Ans}=\left[\sum_{i}\binom{n}{i}\mathbf{A}^i[k|i]\right]_{1,1}\]

考虑形如 \([k|i]\) 的式子使用单位根反演化简,有:

\[\begin{aligned}\mathrm{Ans}&=\left[\sum_{i}\binom{n}{i}\mathbf{A}^i\frac{1}{k}\sum_{j=0}^{k-1}\left(\omega_{k}^{j}\right)^{i}\right]_{1,1}\\&=\frac{1}{k}\left[\sum_{j=0}^{k-1}\sum_{i}\binom{n}{i}\left(\omega_{k}^{j}\mathbf{A}\right)^{i}\right]_{1,1}\end{aligned}\]

用二项式定理化简,其中 \(\mathbf{I}\) 是单位矩阵 \(\begin{bmatrix}1&0\\0&1\end{bmatrix}\)

\[\begin{aligned}\mathrm{Ans}&=\frac{1}{k}\left[\sum_{j=0}^{k-1}\sum_{i}\binom{n}{i}\left(\omega_{k}^{j}\mathbf{A}\right)^{i}\right]_{1,1}\\&=\frac{1}{k}\left[\sum_{j=0}^{k-1}\left(\mathbf{I}+\omega_{k}^{j}\mathbf{A}\right)^n\right]_{1,1}\end{aligned}\]

最后,考虑可行性,因为 \(p\equiv 1\pmod{k}\),即 \(k\)\(\varphi(p)\) 的因数,求出 \(p\) 的原根 \(g\) 之后,则 \(g^{\frac{\varphi(p)}{k}}\) 即可当作模意义下的 \(\omega_{k}\)

代码如下:

#include <cstdio>

typedef long long LL;

LL N;
int K, P, G;

inline int qPow(int b, LL e) {
    int a = 1;
    for (; e; e >>= 1, b = (LL)b * b % P)
        if (e & 1) a = (LL)a * b % P;
    return a;
}

inline void getG() {
    int X = P - 1, Y = X, t = 0;
    static int p[10];
    for (int i = 2; i * i <= Y; ++i) {
        if (Y % i) continue;
        p[++t] = i;
        while (Y % i == 0) Y /= i;
    } if (Y > 1) p[++t] = Y;
    for (int g = 2; ; ++g) {
        int ok = 1;
        for (int i = 1; i <= t; ++i)
            if (qPow(g, X / p[i]) == 1) { ok = 0; break; }
        if (ok) { G = qPow(g, X / K); break ; }
    }
}

struct Mat {
    int A11, A12, A21, A22;
    Mat() {}
    Mat(int A11, int A12, int A21, int A22) :
        A11(A11), A12(A12), A21(A21), A22(A22) {}
    inline friend Mat operator +(Mat A, Mat B) {
        return Mat((A.A11 + B.A11) % P, (A.A12 + B.A12) % P, (A.A21 + B.A21) % P, (A.A22 + B.A22) % P);
    }
    inline friend Mat operator *(Mat A, Mat B) {
        return Mat(
            ((LL)A.A11 * B.A11 + (LL)A.A12 * B.A21) % P,
            ((LL)A.A11 * B.A12 + (LL)A.A12 * B.A22) % P,
            ((LL)A.A21 * B.A11 + (LL)A.A22 * B.A21) % P,
            ((LL)A.A21 * B.A12 + (LL)A.A22 * B.A22) % P
        );
    }
    inline friend Mat operator ^(Mat B, LL E) {
        Mat A(1, 0, 0, 1);
        for (; E; E >>= 1, B = B * B)
            if (E & 1) A = A * B;
        return A;
    }
};

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%lld%d%d", &N, &K, &P);
        getG();
        Mat I(1, 0, 0, 1), W(G, 0, 0, G), A(1, 1, 1, 0), Ans(0, 0, 0, 0);
        for (int j = 0; j < K; ++j) {
            Ans = Ans + ((I + A) ^ N);
            A = A * W;
        }
        printf("%lld\n", (LL)Ans.A11 * qPow(K, P - 2) % P);
    }
    return 0;
}

转载于:https://www.cnblogs.com/PinkRabbit/p/11044731.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值