[拉格朗日插值]CF622F The Sum of the k-th Powers 题解
好题,基本没有高级知识但是依然能把蒟蒻如我绕的晕头转向
前置知识:拉格朗日插值
这个东西其实也比较简单,一个 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;
}