拉格朗日插值
1 引入
众所周知,给定二维平面上的任意
而拉格朗日插值就是求解多项式插值的一种插值法。
2 拉格朗日插值
2.1 朴素解法
假设我们现在给出的
其中
这个时候我们已经可以满足取值为其他
此时假如我们要查询一个单点
模板题:【模板】拉格朗日插值,代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
const int Mod = 998244353;
int n, k, x[Maxn], y[Maxn];
int qpow(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
int lagrange(int x[], int y[], int X) {
int ans = 0;
for(int i = 1; i <= n; i++) {
int res = 1;
for(int j = 1; j <= n; j++) {
if(i == j) continue;
res = res * (X - x[j]) % Mod * qpow(x[i] - x[j], Mod - 2) % Mod;
}
ans = (ans + res * y[i] % Mod + Mod) % Mod;
}
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
cout << lagrange(x, y, k) << '\n';
return 0;
}
2.2 线性求值解法
上面的朴素解法单次查询的复杂度是
考虑到上面构造出来的多项式本质上是:
考虑令
这里说一下怎样直接除以多项式
。 我们先看常数项
,显然它的值就是 ,想要实现除掉多项式 的效果直接给 除以 即可。 然后看一次项
,我们同样直接给它除以一个 ,但是此时会发现一个问题,即 中本身有一项为 ,我们需要先将这个值减去才能除以 。不过好巧不巧,我们发现此时的 就是这个值,所以新的 就是 减去 后再除以 。 对于后面的每一项都是同理,因此可以得出
。
代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
const int Mod = 998244353;
int n, k, x[Maxn], y[Maxn];
int qpow(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
int t[Maxn], f[Maxn], g[Maxn], fg[Maxn];
void init(int x[], int y[]) {
for(int i = 1; i <= n; i++) {
t[i] = 1;
for(int j = 1; j <= n; j++) {
if(i == j) continue;
t[i] = t[i] * (x[i] - x[j]) % Mod;
}
t[i] = qpow(t[i], Mod - 2) * y[i] % Mod;//求出 t[i]
}
fg[1] = 1;
for(int i = 1; i <= n; i++) {
for(int j = n; j >= 1; j--) fg[j] = (fg[j - 1] + fg[j] * (Mod - x[i]) % Mod) % Mod;//先求出所有 (x - x[i]) 的积
}
for(int i = 1; i <= n; i++) {
int inv = qpow(Mod - x[i], Mod - 2);
for(int j = 1; j <= n; j++) {
g[j] = (fg[j] - g[j - 1]) * inv % Mod;//利用上面的式子
f[j] = (f[j] + g[j] * t[i] % Mod + Mod) % Mod;//乘上 t[i] 之后贡献给对应系数
}
}
}
int lagrange(int X) {
int ans = 0, res = 1;
for(int i = 1; i <= n; i++) {//多项式求值
ans = (ans + res * f[i] % Mod) % Mod;
res = res * X % Mod;
}
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
init(x, y);
cout << lagrange(k) << '\n';
return 0;
}
2.3 横坐标连续的插值
在某些题目中,我们获得的
考虑回到构造出的函数
此时即可
代码如下:
int y[Maxn], f[Maxn], g[Maxn], pre[Maxn], suf[Maxn];
int qpow(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
void init(int n) {
f[0] = 1;
for(int i = 1; i <= n; i++) f[i] = f[i - 1] * i % Mod;
g[n] = qpow(f[n], Mod - 2);
for(int i = n - 1; i >= 0; i--) g[i] = g[i + 1] * (i + 1) % Mod;
}
int lagrange(int n, int X) {
pre[0] = suf[n + 1] = 1;
for(int i = 1; i <= n; i++) pre[i] = pre[i - 1] * (X - i) % Mod;
for(int i = n; i >= 1; i--) suf[i] = suf[i + 1] * (X - i) % Mod;
int res = 0;
for(int i = 1; i <= n; i++) {
int num = y[i] * pre[i - 1] % Mod * suf[i + 1] % Mod * g[i - 1] % Mod * g[n - i] % Mod;
if((n - i) & 1) res = (res - num + Mod) % Mod;
else res = (res + num) % Mod;
}
return res;
}
下面来看几道经典例题。
3 例题
例 1: [湖北省队互测2014] 一个人的数论
题意: 令
发现
发现整个式子是一个狄利克雷卷积的形式,但是后面自然数的幂次方和并不是一个积性函数。这个时候运用拉格朗日插值,熟知自然数的
于是设该多项式为
这个时候发现后面的和式就是一个积性函数了。由于
直接求解该式子即可,复杂度
通过上例发现,拉格朗日插值的一个经典用途就是求出自然数的幂次方和。
例 2: [JLOI2016] 成绩比较
我们考虑 dp。由于最后一定会有
首先从
发现这个式子的后半部分无法直接求解,因为
例 3: [集训队互测 2012] calc
首先将有序的
考虑对于无序集合怎么做,不难想到设
发现此时直接暴力做是
左边是多项式差分的形式,所以差分过后应该会得到一个
所以可以得到
也就是说,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律