题目链接
- 题意:给定 N , C , K N,C,K N,C,K,求 ( F 0 K + F C K + F 2 C K + . . . + F N C K ) m o d 1 e 9 + 9 ({F_{0}}^K+{F_{C}}^K+{F_{2C}}^K+...+{F_{NC}}^K) \ mod\ 1e9+9 (F0K+FCK+F2CK+...+FNCK) mod 1e9+9,其中 F i F_i Fi是 F i b o n a c c i Fibonacci Fibonacci 数列的第 i i i 项。
思路:
三个前置知识:
- F i b o n a c c i Fibonacci Fibonacci 数列的通项公式: F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n] Fn=51[(21+5)n−(21−5)n]
- 二项式定理: ( a + b ) n = ∑ r = 0 n ( n r ) a r b n − r (a+b)^n=\displaystyle \sum^{n}_{r=0}{ {n \choose r}a^rb^{n-r}} (a+b)n=r=0∑n(rn)arbn−r
- 二次剩余:一个数 a a a,如果不是 p p p 的倍数且模 p p p 同余某个数的平方,则称 a a a 为模 p p p 的二次剩余。
暴力发现 5 是模 1e9+9 的二次剩余,并且发现 38300801 6 2 ≡ 5 m o d 1 e 9 + 9 383008016^2\equiv5 \ mod \ 1e9+9 3830080162≡5 mod 1e9+9
设 F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] = d ( a n − b n ) F_n=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n]=d(a^n-b^n) Fn=51[(21+5)n−(21−5)n]=d(an−bn),那么:
- d = 276601605 ≡ 1 5 m o d 1 e 9 + 9 d=276601605\equiv\frac{1}{\sqrt{5}}\ mod \ 1e9+9 d=276601605≡51 mod 1e9+9
- a = 691504013 ≡ 1 + 5 2 m o d 1 e 9 + 9 a=691504013\equiv\frac{1+\sqrt{5}}{2}\ mod \ 1e9+9 a=691504013≡21+5 mod 1e9+9
- b = 308495997 ≡ 1 − 5 2 m o d 1 e 9 + 9 b=308495997\equiv\frac{1-\sqrt{5}}{2}\ mod \ 1e9+9 b=308495997≡21−5 mod 1e9+9
所以我们可以得到:
F 0 K + F C K + F 2 C K + . . . + F N C K = ∑ i = 0 N F i C K = ∑ i = 0 N [ d ( a i C − b i C ) ] k = ∑ i = 0 N d k ∑ j = 0 K ( K j ) ( − 1 ) K − j ( a i C ) j ( b i C ) K − j = d k ∑ j = 0 K ( K j ) ( − 1 ) K − j ∑ i = 0 N ( ( a j b K − j ) C ) i {F_{0}}^K+{F_{C}}^K+{F_{2C}}^K+...+{F_{NC}}^K\\=\displaystyle \sum^{N}_{i=0}{ {F_{iC}}^{K}}=\displaystyle \sum^{N}_{i=0}{[d(a^{iC}-b^{iC})]^k}\\=\displaystyle \sum^{N}_{i=0}{d^k \displaystyle \sum^{K}_{j=0}{ {K \choose j}(-1)^{K-j}(a^{iC})^j(b^{iC})^{K-j}}}\\=d^k\displaystyle \sum^{K}_{j=0}{ {K \choose j}(-1)^{K-j}\displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i} F0K+FCK+F2CK+...+FNCK=i=0∑NFiCK=i=0∑N[d(aiC−biC)]k=i=0∑Ndkj=0∑K(jK)(−1)K−j(aiC)j(biC)K−j=dkj=0∑K(jK)(−1)K−ji=0∑N((ajbK−j)C)i
可以发现 ∑ i = 0 N ( ( a j b K − j ) C ) i \displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i i=0∑N((ajbK−j)C)i是个等比数列, q = ( a j b K − j ) C q=(a^{j}b^{K-j})^C q=(ajbK−j)C
根据等比数列前 n n n 项和公式可以求得 S n = ∑ i = 0 N ( ( a j b K − j ) C ) i = q ( q N − 1 ) q − 1 Sn=\displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i=\frac{q(q^N-1)}{q-1} Sn=i=0∑N((ajbK−j)C)i=q−1q(qN−1)
- 这样的话,复杂度貌似是 O ( K ∗ l o g ( N ) ) O(K*log(N)) O(K∗log(N))
问题1:如何 O ( 1 ) O(1) O(1) 求组合数取模呢?
已知组合数公式: ( n m ) = n ! ( n − m ) ! m ! {n\choose m}=\frac{n!}{(n-m)!m!} (mn)=(n−m)!m!n!,因为 K K K 的范围只有 1e5,所以我们可以 O ( K ) O(K) O(K) 预处理求得 1e5 内的阶乘 f a c [ i ] fac[ i ] fac[i],逆元 i n v [ i ] inv[i] inv[i] 以及阶乘的逆元 F a c I n v [ i ] FacInv[i] FacInv[i]。
【用到了线性递推求逆元;
阶乘的逆元就递推即可 : F a c I n v [ i ] = F a c I n v [ i − 1 ] ∗ i n v [ i ] FacInv[i]=FacInv[i-1]*inv[i] FacInv[i]=FacInv[i−1]∗inv[i]】
问题2:如何 O ( 1 ) O(1) O(1) 求 q = ( a j b K − j ) C q=(a^{j}b^{K-j})^C q=(ajbK−j)C 呢?
- 我们发现当 j ( 0 → K ) j(0\rightarrow K) j(0→K) 时,q也是个等比数列,公比为 a c b c \displaystyle\frac{a^c}{b^c} bcac,所以我们可以循环前将 a c b c \displaystyle\frac{a^c}{b^c} bcac 算出来,然后每次循环乘上该公比即可。
- 初始 q = ( a 0 b K ) C = ( b K ) C q=(a^{0}b^{K})^C=(b^K)^C q=(a0bK)C=(bK)C,对于 b K b^{K} bK 我们还是可以 O ( K ) O(K) O(K) 预处理出幂的。
优化:欧拉降幂
我们发现 N , C N,C N,C的范围都是1e18,太大了。
因为 p = 1 e 9 + 9 p=1e9+9 p=1e9+9 是个质数,和所有数都互质,所以我们可以利用欧拉降幂来优化快速幂: a c ≡ a c % ψ ( p ) m o d p a^c\equiv a^{c \ \% \ \psi(p)} \ mod \ p ac≡ac % ψ(p) mod p , 其中 ψ ( p ) = p − 1 = 1 e 9 + 8 \psi(p)=p-1=1e9+8 ψ(p)=p−1=1e9+8
Code:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define INF 0x3f3f3f3f
#define A 691504013
#define B 308495997
#define inv_rt_5 276601605
#define faiMod 1000000008
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read()
{
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
const ll mod = 1000000009;
ll fpow(ll x, ll y) {
ll base = x % mod, ans = 1;
y %= faiMod;
while(y) {
if(y & 1) ans *= base, ans %= mod;
base *= base, base %= mod;
y >>= 1;
}
return ans % mod;
}
//ll fd, a, b, d;
//void Find() {
// for(ll i = sqrt(mod);; ++ i ) {
// if(i * i % mod == 5) {
// fd = i; break;
// }
// }
// d = fpow(fd, mod - 2);
// a = (1 + fd) * fpow(2, mod - 2) % mod;
// b = (1 - fd + mod) * fpow(2, mod - 2) % mod;
//}
ll N, C, K;
ll powA[maxN], powB[maxN];
ll fac[maxN], inv[maxN], FacInv[maxN];
void Init() {
fac[0] = fac[1] = inv[1] = FacInv[0] = FacInv[1] = 1;
for(int i = 2; i <= 100000; ++ i ) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
FacInv[i] = FacInv[i - 1] * inv[i] % mod;
}
powA[0] = powB[0] = 1;
for(int i = 1; i <= 100000; ++ i ) {
powA[i] = powA[i - 1] * A % mod;
powB[i] = powB[i - 1] * B % mod;
}
}
ll getNCM(ll n, ll m) {
return fac[n] * FacInv[m] % mod * FacInv[n - m] % mod;
}
int main()
{
// Find();
Init();
const ll base = A * fpow(B, mod - 2) % mod;
ll t; t = read();
while(t -- ) {
N = read(), C = read(), K = read();
ll ans = fpow(inv_rt_5, K) % mod, sum = 0;
ll q = fpow(powA[0] * powB[K], C);
ll inc = fpow(base, C);
for(ll i = 0; i <= K; ++ i ) {
ll nCm = getNCM(K, i);
if((K - i) & 1) nCm = mod - nCm;
ll Sn = q;
if(q == 1) {
Sn = N % mod;
} else {
Sn *= (fpow(q, N) - 1), Sn %= mod;
Sn *= fpow(q - 1, mod - 2), Sn %= mod;
}
sum += Sn * nCm, sum %= mod;
q *= inc, q %= mod;
}
ans *= sum, ans %= mod;
cout << ans << endl;
}
return 0;
}