1. 程式人生 > 實用技巧 >斐波那契 + 二次剩餘

斐波那契 + 二次剩餘

由hdu多校自閉場衍生出的幾題

題目1

\(\sum_{I = 1}^nF_i^k\),最後mod 1e9 + 9
感覺像是矩陣快速冪,其實不是
\(F_{n} = \frac{(\frac{1+\sqrt5}{2})^n- (\frac{1-\sqrt 5}{2})^n}{\sqrt 5}\)

因為\(383008016^2 \equiv 5(\mod 10^9 + 9)\)

所以\(383008016 \equiv \sqrt 5(\mod 10^9+9)\)

所以把383008016乘以\(inv(5, 10^9+9)\)得到

\(\frac{\sqrt 5}{5} \equiv 276601605(\mod 10^9+9)\)

同理

\(\frac{1 + \sqrt 5}{5}\equiv 691504013 (\mod 10^9+9)\)

\(\frac{1 - \sqrt 5}{5} \equiv 308495997(\mod 10^9 + 9)\)

那麼\(F_n = 276601605(691504013 ^ n - 308495997 ^ n) ( \mod 10^9 + 9)\)

\(S = \frac{\sqrt 5}{5}, R_1 = \frac{1 + \sqrt5}{2}, R_2 = \frac{1 - \sqrt 5}{2}\)

那麼\(F_n^m = S^m(R_1^n - R_2^n)^m\)

\(F_n^m = S^m\sum_{k = 0} ^m C_m^k(R_1^{nk}R_2^{n(m - k)} * (-1)^{m - k})\)

那麼對於斐波那契前n項和為\(S^m\sum_{k =0 } ^m[C_m^k(\sum_{j = 1}^nR_1^{jk}R_2^{j(m - k)})*(-1)^{m - k}]\)

注意,如果說mod是其他的值,那麼久需要用二次剩餘求出該模下5的一個剩餘

得到答案後,只需要線性遞推出\(C(m, i)\)在mod下的值,以及遍歷最外面的迴圈k,其中內部的小迴圈是一個等比數列,直接快速冪求即可
時間複雜度\(O(mlogn)\)

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int mod = 1e9 + 9;
const int N = 3e5 + 5; 
ll S = 723398404, R1 = 308495997, R2 = 691504013; 
ll F[N], a[N];
ll pow(ll a, ll b, ll p){
    ll ans = 1; a %= p;
    while(b){
        if(b & 1) ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
void ex_gcd(ll a, ll b, ll &d, ll &x, ll &y){
    if(!b) {
        d = a, x = 1, y = 0;
        return;
    }
    ex_gcd(b, a % b, d, y, x);
    y -= x * (a / b);
}
ll cal(ll a, ll b){ //等比數列
    ll ans = 0, sum = a, mul = 1, powe = a;
    while(b){
        if(b & 1) {
            ans = (ans + mul * sum % mod) % mod;
            mul = mul * powe % mod;
        }
        sum = (powe + 1) % mod * sum % mod;
        powe = powe * powe % mod;
        b >>= 1;
    }
    return ans;
}
ll c[N];
int main(){
    int t;
    scanf("%d", &t);
    while(t--) {
        ll ans = 0;
        ll n, m;
        c[0] = 1;
        scanf("%lld%lld", &n, &m);
        for(int i = 1; i <= m; i++) {// 線性求C(m, i)
            ll d, x, y;
            ex_gcd(i, mod, d, x, y); // 求i對mod的逆元
            x = (x % mod + mod) % mod;
            c[i] = (c[i - 1] * x) % mod;
            c[i] = (c[i] * (m - i + 1)) % mod;
        }
        for(int i = 0; i <= m; i++) {
            ll x = c[i];
            x = x * cal(pow(R1, i, mod) * pow(R2, m - i, mod) % mod, n) % mod;
            x = (x % mod + mod) % mod;
            if((m - i) % 2) ans = (ans - x + mod) % mod;
            else ans = (ans + x + mod) % mod;
        }
        ans = ans * pow(S, m, mod) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}