拉格朗日插值
简要阐述
结论给定n+1n + 1n+1个点最多可以得到一个nnn次多项式的表达式,并且f(x)=∑i=1nyi∏j∤ix−xjxi−yjf(x) = \sum_{i = 1} ^{n} y_i \prod\limits_{j \nmid i}\frac{x - x_j}{x_i - y_j}f(x)=∑i=1nyij∤i∏xi−yjx−xj我们随便往里带入一个xi,yjx_i, y_jxi,yj可以得到等式是成立的,所以我们只要套模板即可求得某个函数的值。
特殊情况
当xxx的取值是连续的,我们还可以得到一个更优的算法。
pre[i]=(x−x1)(x−x2)(x−x3)…(x−xi−1)(x−xi),suc[i]=(x−xi)(x−xi+1)(x−xi+2)…(x−xn−1)(x−xn)pre[i] = (x - x_1)(x - x_2) (x - x_3) \dots(x - x_{i - 1})(x - x_i),suc[i] = (x - x_{i})(x - x_{i + 1})(x - x_{i + 2}) \dots (x - x_{n - 1})(x - x_{n})pre[i]=(x−x1)(x−x2)(x−x3)…(x−xi−1)(x−xi),suc[i]=(x−xi)(x−xi+1)(x−xi+2)…(x−xn−1)(x−xn)
所以上面式子的分母可以写成pre[i−1]×suc[i+1]pre[i - 1] \times suc[i + 1]pre[i−1]×suc[i+1],同样的分子写成(−1)n−i(i−1)!(n−i)!(-1) ^{n - i} (i - 1) !(n - i)!(−1)n−i(i−1)!(n−i)!,这个时候提前处理好阶乘逆元,前缀积,后缀积即可达到O(n)O(n)O(n)来求解了。
两个模板题
P4781 【模板】拉格朗日插值
/*Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;
const double eps = 1e-7;const int N = 2e3 + 10, mod = 998244353;ll x[N], y[N], ans, s1, s2, n, k;ll quick_pow(ll a, int n) {ll ans = 1;while(n) {if(n & 1) ans = ans * a % mod;a = a * a % mod;n >>= 1;}return ans;
}ll inv(ll x) {return quick_pow(x, mod - 2);
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);scanf("%lld %lld", &n, &k);for(int i = 1; i <= n; i++) {scanf("%lld %lld", &x[i], &y[i]);}for(int i = 1; i <= n; i++) {s1 = y[i], s2 = 1;for(int j = 1; j <= n; j++) {if(i == j) continue;s1 = (s1 * (k - x[j]) % mod + mod) % mod, s2 = (s2 * (x[i] - x[j]) % mod + mod) % mod;}ans = (ans + s1 * inv(s2) % mod) % mod;}printf("%lld\n", ans);return 0;
}
∑i=1nik\sum\limits_{i = 1} ^{n} i ^ ki=1∑nik
/*Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;
const double eps = 1e-7;const int N = 1e6 + 10, mod = 1e9 + 7;ll fac[N], pre[N], suc[N], inv[N], prime[N], sum[N], n, k, cnt;bool st[N];ll quick_pow(ll a, int n) {ll ans = 1;while(n) {if(n & 1) ans = ans * a % mod;a = a * a % mod;n >>= 1;}return ans;
}void init() {sum[1] = 1;for(int i = 2; i < N; i++) {if(!st[i]) {prime[cnt++] = i;sum[i] = quick_pow(i, k);}for(int j = 0; j < cnt && i * prime[j] < N; j++) {st[i * prime[j]] = 1;sum[i * prime[j]] = 1ll * sum[i] * sum[prime[j]] % mod;if(i % prime[j] == 0) break;}}fac[0] = inv[0] = 1;for(int i = 1; i < N; i++) {sum[i] = (sum[i] + sum[i - 1]) % mod;fac[i] = 1ll * fac[i - 1] * i % mod;}inv[N - 1] = quick_pow(fac[N - 1], mod - 2);for(int i = N - 2; i >= 1; i--) {inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;}
}ll solve(ll n, int k) {ll ans = 0;init();pre[0] = suc[k + 3] = 1;for(int i = 1; i <= k + 2; i++) pre[i] = 1ll * pre[i - 1] * (n - i) % mod;for(int i = k + 2; i >= 1; i--) suc[i] = 1ll * suc[i + 1] * (n - i) % mod;for(int i = 1; i <= k + 2; i++) {ll a = 1ll * pre[i - 1] * suc[i + 1] % mod, b = 1ll * inv[i - 1] * inv[k + 2 - i] % mod;if((k + 2 - i) & 1) b *= -1;ans = ((ans + 1ll * sum[i] * a % mod * b % mod) % mod + mod) % mod;}return ans;
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);scanf("%lld %lld", &n, &k);printf("%lld\n", solve(n, k));return 0;
}