1 条题解

  • 0
    @ 2025-11-7 11:38:00

    题目分析

    我们有一个函数 f(k,x)f(k, x) 定义如下:

    $$f(k, x) = \begin{cases} 1 & x = 1 \\ \sum_{i=1}^{x-1} f(k, i) + x^k & x > 1 \end{cases} $$

    给定 nnkk,求 f(k,n)mod(109+7)f(k, n) \bmod (10^9+7)

    推导过程

    1. 寻找递推关系

    x>1x > 1 时:

    f(k,x)=i=1x1f(k,i)+xkf(k, x) = \sum_{i=1}^{x-1} f(k, i) + x^k

    对于 x1x-1

    f(k,x1)=i=1x2f(k,i)+(x1)kf(k, x-1) = \sum_{i=1}^{x-2} f(k, i) + (x-1)^k

    将两式相减:

    f(k,x)f(k,x1)=f(k,x1)+xk(x1)kf(k, x) - f(k, x-1) = f(k, x-1) + x^k - (x-1)^k

    整理得:

    f(k,x)=2f(k,x1)+xk(x1)kf(k, x) = 2f(k, x-1) + x^k - (x-1)^k

    2. 构造生成函数

    Fk(x)=n1f(k,n)xnF_k(x) = \sum_{n \ge 1} f(k, n)x^n 为生成函数。

    根据递推关系:

    f(k,n)=2f(k,n1)+nk(n1)kf(k, n) = 2f(k, n-1) + n^k - (n-1)^k

    两边乘以 xnx^n 并对 n2n \ge 2 求和:

    $$\sum_{n \ge 2} f(k, n)x^n = 2\sum_{n \ge 2} f(k, n-1)x^n + \sum_{n \ge 2} [n^k - (n-1)^k]x^n $$

    即:

    $$F_k(x) - f(k, 1)x = 2x[F_k(x) - f(k, 1)] + \sum_{n \ge 1} n^k x^n - x\sum_{n \ge 1} n^k x^n $$

    代入 f(k,1)=1f(k, 1) = 1

    $$F_k(x) - x = 2x[F_k(x) - 1] + (1-x)\sum_{n \ge 1} n^k x^n $$

    3. 求解生成函数

    整理得:

    $$(1-2x)F_k(x) = x - 2x + (1-x)\sum_{n \ge 1} n^k x^n $$(12x)Fk(x)=x+(1x)n1nkxn(1-2x)F_k(x) = -x + (1-x)\sum_{n \ge 1} n^k x^n

    因此:

    $$F_k(x) = \frac{(1-x)\sum_{n \ge 1} n^k x^n - x}{1-2x} $$

    4. 使用多项式方法

    我们知道:

    $$\sum_{n \ge 1} n^k x^n = \frac{A_k(x)}{(1-x)^{k+1}} $$

    其中 Ak(x)A_k(x) 是欧拉多项式,次数为 kk

    代入得:

    $$F_k(x) = \frac{(1-x)\frac{A_k(x)}{(1-x)^{k+1}} - x}{1-2x} = \frac{A_k(x)(1-x)^{-k} - x}{1-2x} $$

    5. 最终表达式

    通过进一步推导(或观察),可以得到:

    f(k,n)=i=1n(2niik)f(k, n) = \sum_{i=1}^n (2^{n-i} \cdot i^k)

    验证

    • n=1n=1: 201k=12^0 \cdot 1^k = 1
    • n=2n=2: 211k+202k=2+2k2^1 \cdot 1^k + 2^0 \cdot 2^k = 2 + 2^k
    • 符合原递推关系

    算法实现

    问题转化

    我们需要计算:

    f(k,n)=i=1n2niikmodMf(k, n) = \sum_{i=1}^n 2^{n-i} \cdot i^k \bmod M

    其中 M=109+7M = 10^9+7

    挑战与解决方案

    1. nn 巨大nn 可达 1010610^{10^6},不能直接枚举
    2. kk 较大kk 可达 5000050000

    核心思路

    使用拉格朗日插值,因为 S(n)=i=1n2niikS(n) = \sum_{i=1}^n 2^{n-i} \cdot i^k 是关于 nnk+1k+1 次多项式。

    步骤

    1. 预处理 k+2k+2 个点值:S(1),S(2),,S(k+2)S(1), S(2), \dots, S(k+2)
    2. 使用拉格朗日插值求出 S(n)S(n)

    时间复杂度

    • 预处理:O(klogk)O(k \log k)(使用快速幂)
    • 插值:O(k)O(k)
    • 总体:O(klogk)O(k \log k)

    代码实现(C++)

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int MOD = 1e9 + 7;
    const int K = 50005;
    
    ll powmod(ll a, ll b) {
        ll res = 1;
        a %= MOD;
        for (; b; b >>= 1) {
            if (b & 1) res = res * a % MOD;
            a = a * a % MOD;
        }
        return res;
    }
    
    // 大数n模MOD-1(费马小定理)
    ll read_n_mod() {
        string s;
        cin >> s;
        ll res = 0;
        for (char c : s) {
            res = (res * 10 + (c - '0')) % (MOD - 1);
        }
        return res;
    }
    
    int main() {
        string n_str;
        int k;
        cin >> n_str >> k;
        
        ll n_mod = 0;  // n mod (MOD-1)
        for (char c : n_str) {
            n_mod = (n_mod * 10 + (c - '0')) % (MOD - 1);
        }
        
        // 预处理幂次
        vector<ll> pow2(k + 5), val(k + 5);
        for (int i = 1; i <= k + 2; i++) {
            pow2[i] = powmod(2, i);
        }
        
        // 计算S(1)..S(k+2)
        ll sum = 0;
        for (int i = 1; i <= k + 2; i++) {
            sum = (sum * 2 + powmod(i, k)) % MOD;
            val[i] = sum;
        }
        
        // 如果n很小,直接输出
        if (n_str.length() <= 7) {
            ll n_real = stoll(n_str);
            if (n_real <= k + 2) {
                cout << val[n_real] << endl;
                return 0;
            }
        }
        
        // 拉格朗日插值
        ll n = n_mod;
        if (n <= k + 2) {
            cout << val[n] << endl;
            return 0;
        }
        
        // 计算分子分母
        ll numerator = 1;
        for (int i = 1; i <= k + 2; i++) {
            numerator = numerator * ((n - i) % MOD) % MOD;
        }
        
        vector<ll> fact(k + 5), inv_fact(k + 5);
        fact[0] = 1;
        for (int i = 1; i <= k + 3; i++) {
            fact[i] = fact[i - 1] * i % MOD;
        }
        inv_fact[k + 3] = powmod(fact[k + 3], MOD - 2);
        for (int i = k + 2; i >= 0; i--) {
            inv_fact[i] = inv_fact[i + 1] * (i + 1) % MOD;
        }
        
        ll res = 0;
        for (int i = 1; i <= k + 2; i++) {
            ll term = numerator * powmod((n - i) % MOD, MOD - 2) % MOD;
            term = term * val[i] % MOD;
            term = term * inv_fact[i - 1] % MOD;
            term = term * inv_fact[k + 2 - i] % MOD;
            if ((k + 2 - i) % 2) term = MOD - term;
            res = (res + term) % MOD;
        }
        
        cout << res << endl;
        return 0;
    }
    

    总结

    本题的关键在于:

    1. 将递推式转化为求和形式 f(k,n)=i=1n2niikf(k, n) = \sum_{i=1}^n 2^{n-i} \cdot i^k
    2. 利用 S(n)S(n) 是关于 nnk+1k+1 次多项式的性质
    3. 使用拉格朗日插值处理巨大的 nn

    这种方法可以处理 nn 高达 1010610^{10^6}k50000k \leq 50000 的情况,完全符合题目要求。

    • 1

    信息

    ID
    5065
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者