1 条题解
-
0
题目分析
我们有一个函数 定义如下:
$$f(k, x) = \begin{cases} 1 & x = 1 \\ \sum_{i=1}^{x-1} f(k, i) + x^k & x > 1 \end{cases} $$给定 和 ,求 。
推导过程
1. 寻找递推关系
当 时:
对于 :
将两式相减:
整理得:
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(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 $$因此:
$$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}} $$其中 是欧拉多项式,次数为 。
代入得:
$$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. 最终表达式
通过进一步推导(或观察),可以得到:
验证:
- : ✓
- : ✓
- 符合原递推关系
算法实现
问题转化
我们需要计算:
其中 。
挑战与解决方案
- 巨大: 可达 ,不能直接枚举
- 较大: 可达
核心思路
使用拉格朗日插值,因为 是关于 的 次多项式。
步骤:
- 预处理 个点值:
- 使用拉格朗日插值求出
时间复杂度
- 预处理:(使用快速幂)
- 插值:
- 总体:
代码实现(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
信息
- ID
- 5065
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者