HDU 6755 Fibonacci Sum (二项式定理+预处理) 2020杭电多校第一场

原题题面

The Fibonacci numbers are defined as below:
F 0 = 0 , F 1 = 1 F_0=0,F_1=1
F n = F n 2 + F n 1 ( n > 1 ) F_n=F_{n-2}+F_{n-1}(n>1)
Given three integers N N , C C and K ( 1 N , C 1 e 18 , 1 K 1 e 5 ) K(1\leq N,C \leq 1e18,1 \leq K \leq 1e5) , calculate the following summation:
( F 0 ) K + ( F C ) K + ( F 2 C ) K + ( F 3 C ) K . . . + ( F N C ) K (F_0)^K+(F_{C})^K+(F_{2C})^K+(F_{3C})^K...+(F_{NC})^K
Since the answer can be huge, output it modulo 1000000009 1000000009 ( 1 0 9 + 9 10^9+9 ).

输入样例

2
5 1 1
2 1 2

输出样例

12
2

题面分析

众所周知, 斐波那契数列的通项公式是
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)
因为通式带根号,所以要想办法用二次剩余处理,
具体过程见我去年牛客的这篇博客->2019南昌H题
我们可以轻松 求出 5 = 383008016 \sqrt 5= 383008016 或者 616991993 616991993
616991993 616991993 为例,我们可以得到
1 + 5 2 = 691504013 \frac{1+\sqrt 5}{2}=691504013
1 5 2 = 308495997 \frac{1-\sqrt 5}{2}=308495997
1 5 = 276601605 \frac{1}{\sqrt 5}=276601605
我们记 A = 1 + 5 2 , B = 1 5 2 , D = 1 5 A=\frac{1+\sqrt 5}{2},B=\frac{1-\sqrt 5}{2},D=\frac{1}{\sqrt 5} ,则
F n = D ( A n B n ) ( m o d   1 0 9 + 9 ) F_n=D(A^n-B^n)(mod\ 10^9+9)
虽然可以在 O ( l o g n ) O(logn) 的时间内求出单个 F n F_n ,但 N , C N,C 都是 1 e 18 1e18 ,因此考虑别的办法。
注意到 n = 1 n=1 时, F c k = [ D ( A c B c ) ] k F_c^k=[D(A^c-B^c)]^k ,将其展开,由二项式定理可以得到
F c k = D k i = 0 k C k i ( A c ) k i ( B c ) i = D k i = 0 k ( 1 ) i C k i ( A c ) k i ( B c ) i F_c^k=D^k\sum_{i=0}^{k}C_{k}^{i}(A^c)^{k-i}(-B^c)^i=D^k\sum_{i=0}^{k}(-1)^iC_{k}^{i}(A^c)^{k-i}(B^c)^i
n = 2 n=2 时,把 c c 改为 2 c 2c ,可以得到
F 2 c k = D k i = 0 k ( 1 ) i C k i ( A 2 c ) k i ( B 2 c ) i F_{2c}^k=D^k\sum_{i=0}^{k}(-1)^iC_{k}^{i}(A^{2c})^{k-i}(B^{2c})^i
= D k i = 0 k ( 1 ) i C k i ( A c ) k i ( B c ) i ( A c ) k i ( B c ) i =D^k\sum_{i=0}^{k}(-1)^iC_{k}^{i}(A^{c})^{k-i}(B^{c})^i*(A^{c})^{k-i}(B^{c})^i
注意到,当列举 i i 相同的项时, \sum 内的项是个等比。
首项是 ( 1 ) i ( A k i B i ) c (-1)^i(A^{k-i}B^{i})^c ,公比是 ( A k i B i ) c (A^{k-i}B^{i})^c
因此我们可以考虑枚举 k k ,每一次去计算一个等比数列的和即可。
所以我们可以整理式子,得到

i = 0 n F i c K \sum_{i=0}^{n}F_{ic}^K
= D k i = 0 K C k i ( 1 ) i ( A k i B i ) c ( ( ( A k i B i ) c ) n 1 ) ( A k i B i ) c 1 =D^k\sum_{i=0}^{K}C_k^i\frac{(-1)^i(A^{k-i}B^{i})^c*(((A^{k-i}B^{i})^c)^n-1)}{(A^{k-i}B^{i})^c-1}
当然要处理一下公比是1的情况,即 ( A k i B i ) c = 1 (A^{k-i}B^{i})^c=1
此时 \sum 内的项为 C k i n ( 1 ) i ( A k i B i ) c C_k^i*n*(-1)^i(A^{k-i}B^{i})^c
接下来预处理 1 e 5 1e5 的阶乘, 1 e 5 1e5 的阶乘逆元(为了快速处理 C n m C_{n}^{m} )。
当然算到这步可能还会超时。所以依旧需要优化。
优化1:关于计算首项 ( 1 ) i ( A k i B i ) c (-1)^i(A^{k-i}B^{i})^c 和公比的 n n 次方,从 i i 项变为 i + 1 i+1 项时其实相当于乘上了 B A c \frac{B}{A}^c ,这样每次就可以递推 O ( 1 ) O(1) 求出首项(以及公比),同理公比的 n n 次也可以用类似方法处理。
优化2:欧拉降幂。本题的 m o d mod 是个质数,所以可以直接用欧拉降幂 a b a b % ( 1 e 9 + 9 1 ) ( m o d   1 e 9 + 9 ) a^b\equiv a^{b\%(1e9+9-1)}(mod\ 1e9+9)

AC代码(873ms)

#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+9;
const long long MAXN=1e5;
long long factor[MAXN+10];
long long invFactor[MAXN+10];
long long invn[MAXN+10];
const long long A=691504013;
const long long invA=691504012;
const long long B=308495997;
const long long D=276601605;
//F(n)=D(A^n-B^n)
inline long long quick_pow(long long a, long long b)
{
    long long ans=1, base=a;
    while(b!=0)
    {
        if (b&1)
            ans=(long long) ans*base%mod;
        base=(long long) base*base%mod;
        b>>=1;
    }
    return ans;
}
inline long long MOD(long long a, long long b)
{
    a+=b;
    if (a>=mod)
        a-=mod;
    return a;
}
inline void init()
{
    factor[0]=invFactor[0]=invn[0]=factor[1]=invFactor[1]=invn[1]=1;
    for(int i=2; i<=MAXN; i++)
    {
        factor[i]=factor[i-1]*i%mod;
        invn[i]=(long long) (mod-mod/i)*invn[mod%i]%mod;
        invFactor[i]=invFactor[i-1]*invn[i]%mod;
    }
}
inline long long getC(long long m, long long n)
{
    if (n<0 || m<0 || m>n)
        return 0;
    long long ans=factor[n];
    ans=(long long) ans*invFactor[m]%mod;
    ans=(long long) ans*invFactor[n-m]%mod;
    return ans;
}
int main()
{
    init();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        long long n, c, k;
        scanf("%lld%lld%lld", &n, &c, &k);
        long long ans=0;
        long long a1=quick_pow(quick_pow(A, k), c%(mod-1));
        long long q=quick_pow((long long) invA*B%mod, c%(mod-1));
        long long n1=n%mod;
        long long n2=n%(mod-1);
        long long a1power=quick_pow(a1, n2);
        long long qpower=quick_pow(q, n2);
        for(int i=0; i<=k; i++)
        {
            long long sum=getC(i, k);
            if (i&1)
            {
                sum=mod-sum;
            }
            if (a1==1)
            {
                ans=(ans+(long long) sum*n1%mod)%mod;
            }
            else
            {
                sum=(long long) sum*((long long) a1*(a1power-1+mod)%mod)%mod;
                sum=(long long) sum*quick_pow(a1-1, mod-2)%mod;
                ans=MOD(ans, sum);
            }
            a1=(long long) a1*q%mod;
            a1power=(long long) a1power*qpower%mod;
        }
        printf("%lld\n", (long long) ans*quick_pow(D, k)%mod);
    }
}

后记

再也不用long double的快速乘了,我是傻逼。
DrGilbert 2020.7.22

猜你喜欢

转载自blog.csdn.net/oampamp1/article/details/107508328