2019 南昌邀请赛 B. Polynomial(lagrange插值)(模板)

Lagrange插值与多项式前缀和求解
本文介绍了一种使用Lagrange插值法解决多项式前缀和问题的方法,通过计算多项式的第n+2项,然后求出前缀和的多项式,提供了一个AC代码模板,适用于竞赛编程中遇到的类似问题。

2019 南昌邀请赛 B. Polynomial(lagrange插值)(模板)

题目大意

给出一个n次多项式的前n+1项,求多项式的前缀和

解题思路

先用插值求出第n+2项,再用插值求出前缀和的多项式,版题

模板自制。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int mod = 9999991;
int quick_pow(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=1LL*a*ans%mod;
		a=1LL*a*a%mod;
		b>>=1;
	}
	return ans;
}
int inv[mod];
void getInv()
{
	inv[0]=inv[1]=1;
    for(int i=2;i<=1005;i++)
	inv[i] = 1LL*(mod - (mod / i)) * inv[mod % i] % mod;
	for (int i = 1; i <= 1005; ++i)inv[i] = 1LL*inv[i] * inv[i - 1] % mod;
}
namespace lagrange{//calculate k-degree polynomial Prefix sum of n or k_degree poly' value of n
	const int maxk=1000+5;//the order of the polynomial
	int coeff[maxk];int suf[maxk],bef[maxk];
	int mod;
	int k;
	void init_sum(int k_,int f[],int mod_) //need prefix k+2' value
	{
		mod=mod_;
		k=k_+2;
	    for(int i=1;i<=k;i++) coeff[i]=f[i];
	    coeff[0]=0;
	    for(int i=1;i<=k;i++) coeff[i]=(coeff[i-1]+coeff[i])%mod;
	}
	void init_val(int k_,int f[],int mod_) //need prefix k+1's value
	{
		mod=mod_;
		k=k_+1;
		for(int i=1;i<=k;i++) coeff[i]=f[i];
	}
	int calc(int n)
	{
		if(n==0) return 0;
	    if(n<=k) return coeff[n];
	    bef[0] = suf[0] = 1;
		for (int i = 1; i <= k ; ++i) {
			bef[i] = 1LL*bef[i - 1] * ((n - i) % mod) % mod;
			suf[i] = 1LL*suf[i - 1] * ((n + i - k - 1) % mod) % mod;
		}
		int res = 0;
		for (int i = 1; i <= k ; ++i) {
			int s = 1LL*coeff[i] * bef[i - 1] % mod * suf[k - i ] % mod * inv[i - 1] % mod * inv[k - i] % mod;
			if ((k - i) & 1)s = -s;
			res += s;
			res = (res % mod + mod) % mod;
		}
		return res;
	}
}
int f[1005];
int main()
{
	int t;
	scanf("%d",&t);
	getInv();
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n+1;i++)
		{
			scanf("%d",&f[i]);
		}
		lagrange::init_val(n,f,mod);
		f[n+2]=lagrange::calc(n+2);
		lagrange::init_sum(n,f,mod);
		int L,R;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&L,&R);
			printf("%d\n",(lagrange::calc(R+1)-lagrange::calc(L)+mod)%mod);
		}
	}
}
### Lagrange 插值多项式的定义 Lagrange 插值是一种用于构建通过一组离散数据点的多项式的方法。假设有一组 \( n \) 个互不相同的节点 \( (x_i, y_i) \),其中 \( i = 0, 1, ..., n-1 \),则可以通过这些点构造一个唯一的次数不超过 \( n-1 \) 的多项式 \( P(x) \)[^4]。 该多项式的形式可以写成: \[ P(x) = \sum_{i=0}^{n-1} y_i L_i(x) \] 其中,\( L_i(x) \) 是第 \( i \) 项的基础插值多项式,其定义为: \[ L_i(x) = \prod_{\substack{0 \leq j \leq n-1 \\ j \neq i}} \frac{x - x_j}{x_i - x_j} \][^1]。 --- ### Lagrange 插值多项式的实现 以下是几种常见编程语言中的 Lagrange 插值多项式实现方法: #### MATLAB 实现 MATLAB 中可以直接编写函数来完成 Lagrange 插值。以下是一个简单的实现代码[^1]: ```matlab function y = lagrange_fun(x, xi, yi) y = 0; n = length(xi); for i = 1:n t = 1; for j = 1:n if j ~= i t = t * (x - xi(j)) / (xi(i) - xi(j)); end end y = y + yi(i) * t; end end ``` 此函数接受三个参数:`x` 表示待求解的自变量位置,`xi` 和 `yi` 分别是已知样本点的横坐标和纵坐标数组。 --- #### Python 实现 Python 提供了一种简洁的方式实现 Lagrange 插值多项式[^2]。下面是一段基于 NumPy 的实现代码: ```python import numpy as np from numpy.polynomial import Polynomial as P def lagrange_intp(x: np.ndarray, y: np.ndarray): ''' 返回基于x,y点对的拉格朗日插值多项式 x,y必须有相同的长度 ''' n = len(x) ret = P([0]) for k in range(n): roots = list(x) s = roots.pop(k) p = P.fromroots(roots) p *= y[k] / p(s) ret += p return ret ``` 这段代码的核心思想是对每个采样点分别构造对应的基函数并累加到最终的结果中。 --- #### Java 实现 对于 Java 用户来说,也可以轻松实现 Lagrange 插值功能[^3]。这里给出一种基本版本的实现逻辑: ```java public class LagrangeInterpolation { public static double interpolate(double[] x, double[] y, double targetX) { int n = x.length; double result = 0.0; for (int i = 0; i < n; i++) { double term = y[i]; for (int j = 0; j < n; j++) { if (j != i) { term *= (targetX - x[j]) / (x[i] - x[j]); } } result += term; } return result; } } ``` 以上代码实现了单次查询目标 \( x \) 处对应插值结果的功能。 --- #### C++ 或 C 实现 如果需要在低级语言环境下运行,则可参考如下 C 风格的实现方案[^5]: ```c++ void draw_lagrange_curve(double* x, double* y, int num_points, CDC* dc) { // 绘制原始点 CPen redPen(PS_SOLID, 1, RGB(255, 0, 0)); CBrush redBrush(RGB(255, 0, 0)); CPen* oldPen = dc->SelectObject(&redPen); CBrush* oldBrush = dc->SelectObject(&redBrush); for (int i = 0; i < num_points; ++i) { dc->Ellipse(static_cast<int>(x[i] - 5), static_cast<int>(y[i] - 5), static_cast<int>(x[i] + 5), static_cast<int>(y[i] + 5)); } dc->SelectObject(oldPen); dc->SelectObject(oldBrush); // 计算并绘制曲线上的每一个像素点 for (double t = x[0]; t <= x[num_points - 1]; t += 0.1) { double interpolated_y = 0.0; for (int j = 0; j < num_points; ++j) { double base_function_value = 1.0; for (int m = 0; m < num_points; ++m) { if (m != j) { base_function_value *= (t - x[m]) / (x[j] - x[m]); } } interpolated_y += y[j] * base_function_value; } dc->SetPixel(static_cast<int>(t), static_cast<int>(interpolated_y), RGB(0, 0, 0)); } } ``` 注意这里的绘图部分依赖于 Windows API (`CDC`) 来操作图形界面。 --- ### 总结 无论是哪种语言环境下的实现,核心思路都是围绕着如何高效地计算每一点处的插值权重因子,并将其组合形成完整的插值多项式表达式。此外,在实际应用过程中还需要考虑数值稳定性等问题以提高精度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值