斐波那契 + 二次剩餘
由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; }