拉格朗日插法的应用(ACM,2019南昌邀请赛B题)

首先来了解拉格朗日插值法。

可理解为:

个点为(xi,yi),这个多项式可以看做n个下面的函数的和。其中,第i个多项式函数fi除了在fi(xi)=yi外,其他的位置取值都为0,即fi(xj)=0(i≠j)。

f(x)=a(x−x1)(x−x2)

显然在f(x)=0
时,必有x1,x2
两解。推而广之,一个有n个顶点的n次多项式可以写为, 
f(x)=a(x−x1)(x−x2)……(x−xn)
那么插值用的第i个多项式可写作为 :

又有fi(xi)=yi,带入xi,可得:

即可得:

即插值公式为:

扫描二维码关注公众号,回复: 8780192 查看本文章

如果还不理解的话,举个栗子。

eg:比方说我们已经得到了三个带点(2,5),(3,7),(9,11)

我们利用公式:f(k)=5*\frac{(k-3)*(k-9)}{(2-3)*(2-9)}+7*\frac{(k-2)*(k-9)}{(3-2)*(3-9)}+11*\frac{(k-2)*(k-3)}{(9-2)*(9-3)}就能得出结果了。

附上一道例题,题目来源:https://nanti.jisuanke.com/t/40254 

这是2019南昌邀请赛现场赛的B题。

f(x)是n次多项式,有n+1个未知数,已知n+1个方程,所以可以f(x)是确定的。从L到R的f(x)的总和。

因为题目中给定的是从0-n的f(x)的值,因为0-n都是连续的可以进行预处理来写,此公式可以化成:

pre[i]=\prod_{j=0}^{i}(k-j)

suff[i]=\prod_{j=i}^{n}(k-j)

fact[i]就是阶乘i!

f(k)=\sum_{i=0}^{n}yi*\frac{pre[i-1]*suff[i+1]}{fact[i]*fact[n-i]}

但是分母fact[n-i]可能出现负数,需要去判断,当 n-i 为奇数时,分母应该取负号。

finv[i]是阶乘 i !的逆元,因为的阶乘分之一,这边因为mod=9999991是一个质数,求逆元。

因为时间的关系不能用暴力求解,从而可以使用到前缀和,因为是连续的,变量只有yi,用sum[i]表示前缀和来做。

由于n次多项式的前缀和是n+1次的多项式,也就是说 f(x) 要通过 n+2 个点来求出,然而题目只给出了n+1 个点,所以我们增加一个a[n+1],之后就可以进行插值了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=9999991;
ll a[1010],sum[1010],fact[1010],pre[1010],suff[1010],finv[1010];
ll quickpow(ll a,ll b)
{
    ll res=1;
	while(b)
	{
		if(b&1)
		res=(res*a)%mod;
		b>>=1;
		a=(a*a)%mod; 
	}
	return res%mod;
}
void init()
{
	fact[0]=1;
	int i,j,k;
	for(i=1;i<1010;i++)
	{
		fact[i]=fact[i-1]*i%mod;
	}	
	finv[1009]=quickpow(fact[1009],mod-2);
	for(i=1008;i>=0;i--)
	{
		finv[i]=finv[i+1]*(i+1)%mod;
	}
}
ll solve(ll hh[],ll n,ll x)
{
	if(x<=n)
	return hh[x];
	int i,j,k;
	pre[0]=1,suff[n]=1;
	   int ans=0;
    for(int i=1;i<=n;i++)
        pre[i]=ll(pre[i-1]*(x-(i-1)))%mod;
    for(int i=n-1;i>=0;i--)
        suff[i]=ll(suff[i+1]*(x-(i+1)))%mod;
    for(int i=0;i<=n;i++)
	{
        int fuhao=(n-i)&1?-1:1;
        ans+=hh[i]%mod*fuhao*pre[i]%mod*suff[i]%mod*finv[i]%mod*finv[n-i]%mod;
        ans=(ans%mod+mod)%mod;
    }
    return ans;
}
int main()
{
	init();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,m,i,j;
		scanf("%d %d",&n,&m);
		for(i=0;i<=n;i++)
		{
			scanf("%d",&a[i]);
		}
		a[n+1]=solve(a,n,n+1);
		sum[0]=a[0];
		for(i=1;i<=n+1;i++)
		{
			sum[i]=(sum[i-1]+a[i])%mod;
		}
		while(m--)
		{
			int left,right;
			scanf("%d %d",&left,&right);
			ll ans=(solve(sum,n+1,right)-solve(sum,n+1,left-1));
			ans=(ans%mod+mod)%mod;
			printf("%lld\n",ans);
		}
	}
	return 0;
} 
发布了56 篇原创文章 · 获赞 17 · 访问量 2351

猜你喜欢

转载自blog.csdn.net/weixin_43958964/article/details/97656291