好题,基本没有高级知识但是依然能把蒟蒻如我绕的晕头转向
前置知识:拉格朗日插值
这个东西其实也比较简单,一个 n 次多项式,给你 n + 1 个平面上的点,就能求出一条图像
形式化的讲就是下面这个东西
如果有兴趣可以到我主页找详细讲推法的文章
回到本题,我么首先要证一下 \(\sum_{i = 1}^{n} i ^ k\) 是个 k + 1 次多项式
这个拿差分做一下?发现 \(\sum_{i=1}^{n} i ^ k- \sum_{i=1}^{n - 1} i ^ k\) 差分出来是一个 k 次多项式,由于差分后的次数是会降一次(这个我不知道别人是怎么理解的,我反正是从因式分解角度想了一下,发现还挺显然的,书面证明可以看洛谷题解第一篇),所以就是k + 1次多项式
那是不是就证完了,所以我们要取k + 2个点
显然,这道题平面上点的横坐标是题目中给定的 \(i\) (所以 \(x_i\) 就是 \(i\) ), 纵坐标就是 \(\sum_{i = 1}^{n} i ^k\) 。为了方便后续操作,这 k + 2 个点咱们取成连续的。那么答案就是 \(f(n)\)。
这个东西看着比较丑但是实际上还好,由于 k 是 1e6 范围,\(\sum_{i = 0}^{N} y_i\) 我们不用管预处理后面这个玩意即可
上面一层是 $$\prod_{i = 1}^{j - 1} (n - i) \prod_{i = j + 1}^{k + 2}(n - i)$$
下面一层是 $$\prod_{i = 1}^{j - 1} (i - j) \prod_{i = j + 1}^{k + 2}(i - j)$$
那是不是分别预处理一下这两层和那个 \(y\) 就好了。好消息,这个题的取模没有什么恶心人的点。复杂度 \(O(k\log k)\) 。
#include <bits/stdc++.h>#define inv(x)(Pow(x , mod - 2))
typedef long long ll;
const int N = 1e6 + 7;
const ll mod = 1e9 + 7;namespace cf662F {using namespace std;ll n , k , ans = 0 , y[N] , fac[N] , fac_[N] , factorial[N] , neg_factorial[N];inline ll Pow(ll a , ll b) {ll ans = 1;while(b) {if(b & 1) {ans = ans * a % mod;}a = a * a % mod , b >>= 1;} return ans % mod;}void init() {fac[0] = factorial[0] = neg_factorial[0] = 1;for(register ll i = 1; i <= k + 2; ++i) {y[i] = (y[i - 1] + Pow(i , k)) % mod;fac[i] = fac[i - 1] * i % mod; factorial[i] = factorial[i - 1] * (n - i) % mod;neg_factorial[i] = (-neg_factorial[i - 1] * i + mod) % mod;}fac_[k + 3] = 1;for(register ll i = k + 2; i; --i) {fac_[i] = fac_[i + 1] * (n - i) % mod;}}void solve() {ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);cin >> n >> k , init();for(register ll i = 0; i <= k + 2; ++i) {ll up = factorial[i - 1] * fac_[i + 1] % mod;ll down = fac[i - 1] * neg_factorial[abs(i - k - 2)] % mod;ans = (ans % mod + y[i] * up % mod * inv(down) % mod + mod) % mod;}cout << ans << '\n';}
}int main() {cf662F :: solve(); return 0;
}