拉格朗日插值学习笔记

当给出了n个点的坐标,求经过这 n 个点的多项式,以及某个点的点值,可以在 O(N^2) 的时间内求出,而朴素的高斯消元需要O(N^3)

给出  (x_0, y_0),(x_1,y_1)...(x_n,y_n)

我们对于每一个 i,构造一个多项式      l_i(x)=y_i\prod _{j\neq i}\frac{x-x_j}{x_i-x_j}

发现,把 xi 带进去时,函数值是 yi,  而将其他的x带进去时,函数值都是0

于是我们把每个 i 的 li(x)相加,就是最后的多项式了

f(x)=\sum_{i=0}^ny_i\prod _{j\neq i}\frac{x-x_j}{x_i-x_j}

当要求 N 的函数值时,带进去就可以 n^2 做了

ll lagrange(ll k){
    ll ans = 0;
    for(int i=1; i<=n; i++){
        ll s1 = 1, s2 = 1;
        for(int j=1; j<=n; j++){
            if(i != j){
                s1 = mul(s1, k - x[j]);
                s2 = mul(s2, x[i] - x[j]);
            }
        }
        ans = add(ans, mul(y[i], mul(s1, power(s2, Mod-2))));
    } return ans;
}

如果x是连续的话,可以优化到O(n)

也就是求出 所有(N - xj) 的积,然后不要的那项求一个逆元

 分母是 (k-i)! *(i-1)!,要注意一下符号问题

ll lagrange(ll n, ll k){
	ll pre = 1, ans = 0;
	if(n <= k) return f[n];
	for(int i=1; i<=k; i++) pre = mul(pre, n - i);
	for(int i=1; i<=k; i++){
		ll flag = ((k-i) & 1) ? -1 : 1;
		ll inv1 = power(n - i, Mod-2);
		ll inv2 = (flag * power(mul(fac[i-1], fac[k-i]), Mod-2) + Mod) % Mod;
		ans = add(ans, mul(mul(inv1, inv2), mul(f[i], pre)));
	} return ans;
	
}

经典应用

首先讲一个经典应用:计算    \sum_{i=1}^ni^k(n<=1e15)

老祖宗告诉我们,这个东西是个以n为自变量的k + 1次多项式

然后直接带入k+1个点后用拉格朗日插值算即可,复杂度O(k)

也就是说,只要能判断要求的是一个多项式, 我们就可以用拉格朗日来插

最后放两道道模板题 P4593 [TJOI2018]教科书般的亵渎

这道题主要就是求   \sum_{i=1}^ni^k, 直接插就可以了

#include<bits/stdc++.h>
#define N 100
using namespace std;
const int Mod = 1e9+7;
typedef long long ll;
ll a[N], f[N], fac[N]; int T;
ll add(ll a, ll b){ return (a+b) % Mod;}
ll mul(ll a, ll b){ return (a*b) % Mod;}
ll dec(ll a, ll b){ return (a+Mod-b) % Mod;}
ll power(ll a, ll b){
	ll ans = 1; for(;b;b>>=1){
		if(b&1) ans = mul(ans, a);
		a = mul(a, a);
	} return ans;
}
ll lagrange(ll n, ll k){
	ll pre = 1, ans = 0;
	if(n <= k) return f[n];
	for(int i=1; i<=k; i++) pre = mul(pre, n - i);
	for(int i=1; i<=k; i++){
		ll flag = ((k-i) & 1) ? -1 : 1;
		ll inv1 = power(n - i, Mod-2);
		ll inv2 = (flag * power(mul(fac[i-1], fac[k-i]), Mod-2) + Mod) % Mod;
		ans = add(ans, mul(mul(inv1, inv2), mul(f[i], pre)));
	} return ans;
	
}
int main(){
	scanf("%d", &T);
	fac[1] = fac[0] = 1;
	for(int i=2; i<=N-5; i++) fac[i] = mul(fac[i-1], i);
	while(T--){
		ll n, m; scanf("%lld%lld", &n, &m);
		for(int i=1; i<=m; i++) scanf("%lld", &a[i]);
		sort(a+1, a+m+1);
		for(int i=1; i<=m+3; i++) f[i] = add(f[i-1], power(i, m+1));
		ll ans = 0;
		for(int i=0; i<=m; i++){
			ans = add(ans, lagrange(n-a[i], m+3));
			for(int j=i+1; j<=m; j++) ans = dec(ans, power(a[j]-a[i], m+1));
		} printf("%lld\n", (ans % Mod + Mod) % Mod);
	} return 0;
}

[tyvj1858] xlkxc

求  \sum_{i=1}^n\sum_{j=1}^{a+id}\sum_{l=1}^j l^k

首先 求 f(x)=\sum_{i=1}^xi^k,g(x)=\sum_{i=1}^xf(i),F(n)=\sum_{i=1}^ng(a+id)

可以知道 f 是k+1项的多项式, g差分一下就是 f, 所以 g 是 k+2 项的多项式, F是k+3项的多项式

于是把 F的k+4项用 g 插出来, 然后暴力插 F的 第n项

#include<bits/stdc++.h>
#define N 200
using namespace std;
typedef long long ll;
const ll Mod = 1234567891;
int T, k, a, n, d;
ll f[N], g[N], fac[N];
ll add(ll a, ll b){ return (a+b) % Mod;}
ll mul(ll a, ll b){ return (a*b) % Mod;}
ll power(ll a, ll b){
	ll ans = 1; for(;b;b>>=1){
		if(b&1) ans = mul(ans, a);
		a = mul(a, a);
	} return ans;
}
ll lagrange(ll *a, ll k, ll n){
	ll pre = 1, ans = 0;
	if(n <= k) return a[n];
	for(int i=1; i<=k; i++) pre = mul(pre, n - i);
	for(int i=1; i<=k; i++){
		ll inv1 = power(n - i, Mod-2);
		ll inv2 = power(mul(fac[i-1], fac[k-i]), Mod-2);
		ll flag = ((k-i) & 1) ? -1 : 1;
		ans = add(ans, flag * mul(mul(inv1, inv2), mul(a[i], pre)));
		ans = (ans % Mod + Mod) % Mod;
	} return ans;
}
int main(){
	scanf("%d", &T); fac[1] = fac[0] = 1; 
	for(int i=2; i<=N-5; i++) fac[i] = mul(fac[i-1], i);
	while(T--){
		scanf("%d%d%d%d", &k, &a, &n, &d);
		for(int i=1; i<=k+4; i++) f[i] = add(f[i-1], power(i, k));
		for(int i=1; i<=k+4; i++) f[i] = add(f[i], f[i-1]);
		for(int i=0; i<=k+4; i++) g[i] = lagrange(f, k+4, add(a, mul(i, d)));
		for(int i=1; i<=k+4; i++) g[i] = add(g[i], g[i-1]);
		printf("%lld\n", lagrange(g, k+4, n));
	} return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值