拉格朗日插值法

https://www.cnblogs.com/zwfymqz/p/10063039.html

 

觉得把zwfymqz大佬的博客粘上来就差不多了

 

本博客比较浅显,适合入门粗学,具体深入的话就看 attack 大佬的博客(就是上面的链接)吧

 

拉格朗日的公式

 

首先拉格朗日插值法的公式附上:

 

$$A(k)=\sum_{i=1}^{n} y_i \prod_{j=1}^{n} \dfrac{k-x_j}{x_i-x_j}$$

 

这个式子给出来肯定是先懵。没关系,谁看到这玩意儿不会懵呢?

 

拉格朗日的作用

 

解释一下,拉格朗日插值就是给你 n+1 个点,然后让你构造出一个符合这堆点练成的光滑图像的 n 次函数

 

说人话,就是给你 n+1 个点,让你构造出一个 n 次函数,使得这个函数的图像经过坐标轴上的 n+1 个点。

 

那么上面式子里面的 $x_i$ 以及 $y_i$ 就是一个点的坐标啦!

 

拉格朗日的分析

 

然后你自己验证一下就可以发现: 将原来的点带进去, A 函数输出的结果是符合这 n 个点的位置的。

 

为什么? 因为如果你带的点是 i 的话,那么当 $\sum$ 做到第 i 次时,后面的 $\prod$ 算出来的是 1 ,$\sum$的其余项都是 0

 

于是原式成立了,我们也可以拿它来计算别的 k 的函数值了

 

 

拉格朗日题的套路

 

至于这玩意儿拿来出题,无非就是裸题:

 

  给你 n 个点以及正整数 k ,让你用 n 个点求一个函数图像并将 k 带入求值(当然不是真要你求出函数图像,输出答案就好了)

 

这种题目的做法要么就是暴力代入公式,$O(n^2)$ ,要么给你的 n 个点的横坐标是连续的,你可以利用这个性质优化到 $O(n)$ ,至于怎么优化嘛,想想阶乘之类的东西...(具体推导你可以考虑打开文头的链接)

 

至于另一种题型就是让你求 $\sum_{i=1}^{n}i^k$ 的值了,这个东西为什么能用拉格朗日?他和上面讲的东西毫无关联啊!(你问我我问谁...)总之记好这玩意儿可以表示成一个 k+1 次多项式,然后就用 n 的值拿进去代就好了

 

做法么,自然就是枚举出前 k+2 个点,然后看看 n 是否大于 k+2,不大于的话直接出解了,大于的话就是用 k+2 个点跑拉格朗日,n 带入就好了。 并且由于这 k+2 个点的横坐标是连续的,你可以将计算优化到 $O(k)$

 

什么?为什么这 k 个点是连续的? woc 这几个点是你自己选出来的前 k+2 个点啊!

 

但最后还是决定解释一下为什么 $A(n)=\sum_{i=1}^{n}i^k$ 这玩意儿可以表示成一个 k+1 次函数

 

怎么说,我们考虑将 $i^k$ 表示为 $n-(n-i)^k$ ,懂了吧,将 $n-i$ 看做常数(况且 $n-i$ 在这个表达式中本来就是连续的数)然后展开一下就是一个 k 次多项式啦!

 

恩? k 次? 不是 k+1 次么? emmm 我们考虑一下这里 $n^k$  的项其实总共有 n 项,加起来可不就是 $n*n^k=n^{k+1}$ 么? 至于剩下的项次也是可以以此类推的...

 

(有兴趣的读者可以算一下哦,毕竟博主没有算过,算出来piapia 打脸的话还请读者在评论区中指出,但就算剩下的项无法整合成高一阶的项,这里多项式的最高次系数也已经是 k+1 ,满足 k+1 次函数的定义了)

 

神奇的重心拉格朗日

 

然后是某种骚操作,我是搞不大懂,拉格朗日前面加了个重心,说是优化,然后又说每加一个点的复杂度是 $O (n)$ 的(那加 n 个点的复杂度不还是 $n^2$ ?),也不知道真的假的...貌似真的

 

具体到图像上的实现的话有一个动图可以说明(不知道放到cnblogs上能不能动起来):

 

 

重心拉格朗日gif

 

 

怎么说呢...就是慢慢找点呗...

 

然后就 完 了 _(:з」∠)_

 

慢着?板子都不放一个的?

 

$$O(n^2)$$

 1 //by Judge
 2 //by young Judge
 3 #include<iostream>
 4 #include<cstdio>
 5 #define ll long long
 6 using namespace std;
 7 const int M=2111;
 8 const ll mod=998244353;
 9 #ifndef Judge
10 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
11 #endif
12 char buf[1<<21],*p1=buf,*p2=buf;
13 inline ll read(){
14     ll x=0,f=1; char c=getchar();
15     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
16     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
17 } int n; ll k,K,ans,x[M],y[M];
18 inline void AMOD(ll& a,ll& b){ a+=b; if(a>=mod) a%=mod; }
19 inline ll quick_pow(ll x,ll p,ll ans=1){
20     while(p){
21         if(p&1) ans=ans*x%mod;
22         x=x*x%mod,p>>=1;
23     } return ans;
24 }
25 int main(){
26     n=read(),K=read();
27     for(int i=1;i<=n;++i) x[i]=read(),y[i]=read();
28     for(int i=1,j;i<=n;++i){ k=1;
29         for(j=1;j<=n;++j) if(i!=j)
30             k=k*(x[i]+mod-x[j])%mod;
31         k=quick_pow(k,mod-2);
32         for(j=1;j<=n;++j) if(i!=j)
33             k=k*(K+mod-x[j])%mod;
34         k=k*y[i]%mod,AMOD(ans,k);
35     } return printf("%lld\n",ans),0;
36 }

 

$$O(n)$$

 1 //by Judge
 2 #include<iostream>
 3 #include<cstdio>
 4 #define ll long long
 5 using namespace std;
 6 const int M=2111;
 7 const ll mod=998244353;
 8 #ifndef Judge
 9 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
10 #endif
11 char buf[1<<21],*p1=buf,*p2=buf;
12 inline ll read(){
13     ll x=0,f=1; char c=getchar();
14     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
15     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
16 } ll n,K,ans,x[M],y[M],s1[M],s2[M],ifac[M];
17 inline void ADD(ll& a,ll b){ a+=b; if(a>=mod) a%=mod; }
18 int main(){ n=read()-1,K=read(),s2[n+1]=ifac[0]=ifac[1]=1;
19     for(int i=0;i<=n;++i) x[i]=read(),y[i]=read(); s1[0]=(K-x[i])%mod;
20     for(int i=1;i<=n;++i) s1[i]=s1[i-1]*(K-x[i])%mod;
21     for(int i=n;i>=1;--i) s2[i]=s2[i+1]*(K-x[i])%mod;
22     for(int i=2;i<=n;++i) ifac[i]=(mod-mod/i)*ifac[mod%i]%mod;
23     for(int i=2;i<=n;++i) ifac[i]=ifac[i]*ifac[i-1]%mod;
24     for(int i=0;i<=n;++i)
25         ADD(ans,1ll*y[i]*(i?s1[i-1]:1)%mod*s2[i+1]%mod*
26             ifac[i]%mod*((n-i&1)?-1:1)*ifac[n-i]%mod);
27     return !printf("%lld\n",(ans+mod)%mod);
28 }

 

什么?要函数封装的?

 1 ll lagrange(ll n,ll *x,ll *y,ll xi){ ll ans=0;
 2     s1[0]=(xi-x[0])mod,s2[n+1]=ifac[0]=ifac[1]=1;
 3     for(int i=1;i<=n;++i) s1[i]=s1[i-1]*(xi-x[i])%mod;
 4     for(int i=n;i>=0;--i) s2[i]=s2[i+1]*(xi-x[i])%mod;
 5     for(int i=2;i<=n;++i) ifac[i]=(mod-mod/i)*ifac[mod%i]%mod;
 6     for(int i=2;i<=n;++i) ifac[i]=ifac[i]*ifac[i-1]%mod;
 7     for(int i=0;i<=n;++i)
 8         ADD(ans,1ll*y[i]*(i?s1[i-1]:1)%mod*s2[i+1]%mod*
 9             ifac[i]%mod*((n-i&1)?-1:1)*ifac[n-i]%mod);
10     return (ans+mod)%mod;
11 }

 

 

 

数论的东西,不懂没关系,看还是要看看的...

 

转载于:https://www.cnblogs.com/Judge/p/10428378.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值