「学习笔记」拉格朗日插值

「学习笔记」拉格朗日插值

感谢 clf 指出笔误!/qq

一般形式的构造过程

(n+1) 个横坐标不同的点可以唯一确定一个 n 次多项式,假如知道答案是个关于某个值的多项式以及其项数和若干个点,那么就可以拉格朗日插值出多项式,算出答案。

简单来讲,我们需要构造一个 n 次多项式,使得它满足在已知的点 xi 处取值为 yi

构造 fi(x),使得其仅在 xi 处取值为 1,其他 xj,ji 处取值为 0

那么 yifi(x) 仅在 xi 处取值为 yi,其他 xj,ji 处取值为 0

yifi(x) 求和即得到了我们要求的多项式:在 xi 处取值为 yin 次多项式。

如何构造 fi(x)

  • 如果要在 xj,ji 处取值为 0,可以让 (xxj),ji 乘起来,即为 ji(xxj),为描述方便记作 g(x),基于这个式子,不管怎么对其乘除,在 xj,ji 处取值都为 0

  • 如果要在 xi 处取值为 1,则让 fi(x)=g(x)g(xi),这样当 x=xi 时,fi(xi)=gxigxi=1,注意到 xij,ij,所以不会出现除 0 的情况。

综上所述,我们构造出了 fi(x)=jixxjxixj,题目中所求的 n 次多项式即为:

i=1nyifi(x)i=1nyijixxjxixj

xi 是连续的 n 个整数的 O(n) 做法

代入值到 x 中直接对这个式子计算,每一次枚举 i 后,最后一块算分母的逆元,只会算 n 次逆元,时间复杂度为 O(n2)

特别地,若 xi 是连续的 n 个整数,则可以 O(n) 计算。

prei=j=1ixj,sufi=j=i+1nxj,也就是分子的前缀/后缀积,faci=i!

f(x)=i=1nyijixxjxixj=i=1nyiprei1sufi+1faci1facni(1)ni

可以线性递推求逆元,O(n+logmod) 求逆元也是可以接受的,故复杂度为 O(n)

重心拉格朗日插值法

如果每加一次点就要询问,如何解决?推一下式子试试。

f(x)=i=1nyijixxjxixj=i=1nyiji(xxj)ji(xixj)=i=1nyij=1n(xxj)(ji(xixj))(xxi)=j=1n(xxj)i=1nyi(ji(xixj))(xxi)

g=j=1n(xxj),w(i)=jin(xixj)

则原式 =gi=1nyiw(i)(xxi).

每加入一个点,O(n) 计算出 g 并更新一下 w(i)O(n) 计算 f(x) 即可。

栗题一

ABC208F

给定 n,m,k,计算 f(n,m) 的值,模 109+7

f(n,m)={0(n=0)nK(n>0,m=0)f(n1,m)+f(n,m1)(n>0,m>0)

0N1018,0M30,1K2.5×106

易发现答案为 1k,2k,3k,,nkm 阶前缀和的第 n 项值。

考虑 ik 对答案的贡献次数,是对 f0=1,fk=0(k>0) 的数组作 m 阶前缀和后的第 (ni) 项,也就是 fni。其值为 (ni+m1m1)

从组合意义上考虑,作一阶前缀和为 1,1,1,,设后面第 i 阶前缀和的 fj=gi,j,有递推式 gi,j=gi1,j+gi,j1,恰为只能向右、下走的格点计数。

故贡献的次数为 (1,0)(m,ni) 的路径数,即为 (ni+m1m1)

所以答案为:

i=0nik(ni+m1m1)

考虑 ik(ni+m1m1)ik 为关于 ik 次单项式。

(ni+m1m1)=(ni+m1)!(m1)!(ni)!,其中 (ni+m1)! 为关于 i(ni+m1) 次多项式,(ni)! 为关于 i(ni) 次多项式,(m1)! 是常数。

ik(ni+m1m1) 是关于 i(m+k1) 次多项式。

对于 i[0,n],对这个多项式求和即为答案。

其可以写成 ans=a0s0+a1s1+a2s2++am+k1sm+k1 的形式,其中 si=j=0ijk

由于 si 是关于 i(i+1) 次多项式,故答案为关于 n(m+k) 次多项式。

l=m+k+1,选出前 l 个连续的整数,算出其作 m 阶前缀和的答案,拉格朗日插值即可。

时间复杂度 O(mk)

#include<iostream>
#include<cstdio>
#include<algorithm>
typedef long long ll;
const ll mod = 1000000007;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T> T Add(T x, T y) { return (x + y >= mod) ? (x + y - mod) : (x + y); }
template <typename T> T Mod(T x) { return (x >= mod) ? (x - mod) : (x < 0 ? (x + mod) : x); }
template <typename T> T Mul(T x, T y) { return x * y % mod; }
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
ll qpow(ll x, ll y) { ll sumq = 1; while(y) { if(y & 1) sumq = sumq * x % mod; x = x * x % mod; y >>= 1; } return sumq; }
const int N = 2600100;
ll n;
int m, k;
ll a[N], fac[N], inv[N], pre[N], suf[N], ans;
signed main() {
	read(n); read(m); read(k); int l = m+k+1;
	if(!n) { puts("0");	return 0; }
	for(int i = 1; i <= l; ++i) a[i] = qpow(i, k);
	for(int j = 1; j <= m; ++j)
		for(int i = 1; i <= l; ++i)
			a[i] = Add(a[i], a[i-1]);
	inv[0] = fac[0] = 1; for(int i = 1; i <= l; ++i) fac[i] = fac[i-1] * i % mod;
	inv[l] = qpow(fac[l], mod-2); n %= mod;
	for(int i = l-1; i; --i) inv[i] = inv[i+1] * (i+1) % mod;
	pre[0] = 1; for(int i = 1; i <= l; ++i) pre[i] = pre[i-1] * (n - i) % mod;
	suf[l+1] = 1; for(int i = l; i; --i) suf[i] = suf[i+1] * (n - i) % mod;
	for(int i = 1; i <= l; ++i)
		ans = Add(ans, Mod(a[i] * pre[i-1] % mod * suf[i+1] % mod * inv[i-1] % mod * inv[l-i] % mod * (((l-i)&1) ? -1 : 1)));
	printf("%lld\n", ans);
	return 0;
}
posted @   do_while_true  阅读(186)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 47 seconds

点击右上角即可分享
微信分享提示