2020HDU多校——部分题解

第1场

Fibonacci Sum

  • 题意:求 ( f c ) k + ( f 2 c ) k + ( f 3 ∗ c ) k + . . . + ( f n ∗ c ) k (f_{c})^k+(f_{2c})^k+(f_{3*c})^k+...+(f_{n*c})^k (fc)k+(f2c)k+(f3c)k+...+(fnc)k
    • ZOJ上有一道类似的题目:Power of Fibonacci
    • 参考博客,Fibonacci数列的幂和

分析:先从ZOJ上这道题说起,ZOJ上这个题目求的是 ( f 1 ) k + ( f 2 ) k + ( f 3 ) k + . . . + ( f n ) k (f_{1})^k+(f_{2})^k+(f_{3})^k+...+(f_{n})^k (f1)k+(f2)k+(f3)k+...+(fn)k,也就是参数少了一个C,转化到HDU上这道题目只需要加上C的计算即可。

首先,注意到 m o d = 1 0 9 + 9 mod=10^9+9 mod=109+9,斐波那契数列的通项公式为 f n = 1 5 ∗ [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] f_n=\frac{1}{\sqrt5}*[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n] fn=5 1[(21+5 )n(215 )n]

我们令 a = ( 1 + 5 2 ) , b = ( 1 − 5 2 ) a=(\frac{1+\sqrt5}{2}),b=(\frac{1-\sqrt5}{2}) a=(21+5 )b=(215 )
则有 f n = 1 5 ∗ [ a n − b n ] f_n=\frac{1}{\sqrt5}*[a^n-b^n] fn=5 1[anbn]

( f n ) k = ( 1 5 ) k ∗ [ a n − b n ] k ({f_n})^k={(\frac{1}{\sqrt5})}^k*[a^n-b^n]^k (fn)k=(5 1)k[anbn]k

5 5 5是模 1 0 9 + 9 10^9+9 109+9意义下的二次剩余,设 x 2 % m o d = 5 {x^2}\%mod=5 x2%mod=5,可得 x = 383008016 x=383008016 x=383008016 2 2 2在模 1 0 9 + 9 10^9+9 109+9意义下的逆元为 500000005 500000005 500000005,则 a 和 b a和b ab可以表示为: a = ( ( 1 + 383008016 ) ∗ 500000005 % m o d ) = 691504013 a=((1+383008016)*500000005\%mod) = 691504013 a=((1+383008016)500000005%mod)=691504013 b = ( ( 1 − 383008016 ) ∗ 500000005 % m o d ) = 308495997 b=((1-383008016)*500000005\%mod)=308495997 b=((1383008016)500000005%mod)=308495997

另外,将 ( f n ) k = ( 1 5 ) k ∗ ( a n − b n ) k ({f_n})^k={(\frac{1}{\sqrt5})}^k*(a^n-b^n)^k (fn)k=(5 1)k(anbn)k进行二项式展开(暂不考虑常数项),可得
( f n ) k = ( 1 5 ) k ∗ [ C k 0 ( a n ) k ( − b n ) 0 + C k 1 ( a n ) k − 1 ( − b n ) 1 + C k 2 ( a n ) k − 2 ( − b n ) 2 ] + . . . + C k r ( a n ) k − r ( − b n ) r . . . + C k k − 1 ( a n ) 1 ( − b n ) k − 1 + C k k ( a n ) 0 ( − b n ) k ({f_n})^k={(\frac{1}{\sqrt5})}^k*[C_k^0(a^n)^k(-b^n)^0+C_k^1(a^n)^{k-1}(-b^n)^1+C_k^2(a^n)^{k-2}(-b^n)^2]+...+C_k^{r}(a^n)^{k-r}(-b^n)^{r}...+C_k^{k-1}(a^n)^1(-b^n)^{k-1}+C_k^{k}(a^n)^0(-b^n)^{k} (fn)k=(5 1)k[Ck0(an)k(bn)0+Ck1(an)k1(bn)1+Ck2(an)k2(bn)2]+...+Ckr(an)kr(bn)r...+Ckk1(an)1(bn)k1+Ckk(an)0(bn)k

注意到: C k r ( a n ) k − r ( − b n ) r = ( − 1 ) r C k r ( a n ) k − r ( b n ) r = ( − 1 ) r C k r ( a k − r b r ) n C_k^{r}(a^n)^{k-r}(-b^n)^{r}=(-1)^rC_k^{r}(a^n)^{k-r}(b^n)^{r}=(-1)^rC_k^{r}(a^{k-r}b^r)^n Ckr(an)kr(bn)r=(1)rCkr(an)kr(bn)r=(1)rCkr(akrbr)n

对于所有的系数为 ( − 1 ) r C k r (-1)^rC_k^{r} (1)rCkr的项可以进行一次等比数列求和,公比 q = ( a k − r b r ) q=(a^{k-r}b^r) q=(akrbr),此处要注意公比为1时的情况,否则利用求和公式即可。

本题思路,直接枚举r从0到k,每次都进行一次等比数列求和,累加起来即可。

#include 
using namespace std;

#define LL long long

const LL N = 1e6 + 5;
const LL mod = 1e9 + 9;

const LL inv_2 = 500000005;      //inv(2)
const LL sqrt_5 = 383008016;     //sqrt(5)
const LL inv_sqrt_5 = 276601605; //inv(sqrt5)
const LL A = 691504013;          // A = (1+sqrt(5))/2
const LL B = 308495997;          // B = (1-sqrt(5))/2

LL a[N], b[N];
LL inv[N], fac[N], inv_fac[N];

LL get_inv(LL x) //4种逆元求解方法
{
    if (x == 1)
        return 1;
    return (mod - mod / x) * get_inv(mod % x) % mod;
}
void init() //预处理阶乘与逆元
{
    for (LL i = 1; i < N; ++i)
        inv[i] = get_inv(i);
    fac[0] = inv_fac[0] = 1;
    for (LL i = 1; i < N; ++i)
    {
        fac[i] = fac[i - 1] * i % mod;
        inv_fac[i] = inv_fac[i - 1] * inv[i] % mod;
    }
}

LL C(LL n, LL m) //获取组合数
{
    if (m < 0 || n < m)
        return 0;
    return fac[n] * inv_fac[m] % mod * inv_fac[n - m] % mod;
}

LL qpow(LL t, LL n, LL p)
{
    LL ans = 1;
    while (n)
    {
        if (n % 2 == 1)
            ans = (ans * t) % p;
        t = t * t % p;
        n /= 2;
    }
    return ans;
}

LL solve(LL n, LL k)
{
    LL ans = 0;
    for (LL r = 0; r <= k; r++)
    {
        LL q = a[k - r] * b[r] % mod; //公比
        LL C_k_r = C(k, r);
        int flag = (r % 2 == 0) ? 1 : -1;
        if (q == 1)
            ans += flag * n % mod * C_k_r % mod;
        else
            ans += flag * q * (qpow(q, n, mod) + mod - 1) % mod * get_inv(q - 1) % mod * C_k_r % mod;
        (ans += mod) %= mod;
    }
    LL x = inv_sqrt_5;
    ans = ans * qpow(x, k, mod) % mod;
    return (ans % mod + mod) % mod;
}

int main()
{
    init();
    int T;
    cin >> T;
    a[0] = b[0] = 1;
    for (int i = 1; i < N; i++)
        a[i] = a[i - 1] * A % mod, b[i] = b[i - 1] * B % mod;
    while (T--)
    {
        LL n, k;
        cin >> n >> k;
        cout << solve(n, k) << endl;
    }
    return 0;
}

你可能感兴趣的:(2020HDU多校——部分题解)