拉格朗日插值优化
拉格朗日插值优化 #
题单#
拉格朗日插值#
对于一个 次多项式 ,若它经过 个点 。
设其基本多项式:
你会发现这个多项式经过 和 ,我们把所有这些加起来就会得到经过 个点的多项式 :
for(int i=1;i<=n;++i){
int s1=y[i],s2=1;
for(int j=1;j<=n;++j){
if(j==i) continue;
s1=s1*(k-x[j])%mod;
s2=s2*(x[i]-x[j])%mod;
}
ans+=s1*get_c(s2)%mod;
ans=(ans+mod)%mod;
}
#
- 若 是一个关于 的 次函数,则 是关于 的 次函数,即对一个多项式作差分会导致它次数减一,而作前缀和会导致它次数加一。
- 一个 次多项式乘一个 次多项式会得到一个 次多项式。
CF995F Cowmpany Cowmpensation#
设 表示以 为根的子树用值域 染色的方案数,则有:
直接 复杂度是 的,
那么我们就需要一个重要的结论来进行一个优化。
重要结论#
设 的子树大小为 ,则 是关于 的 次函数。
我们考虑采用归纳法证明这一点:
- 对于叶子节点,,成立。
- 对于非叶子节点,考虑将多项式差分一下:
若 满足结论,则 的次数为 ,还原回去就是 。
所以我们可以求出过 的多项式方程并用拉格朗日插值插出 处的值。
可以使用连续插值但是没有必要。
总体复杂度是 的
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e3+5,mod=1e9+7;
int n,d;
vector <int> G[N];
int f[N][N],x[N],y[N];
inline int ksm(int x,int idx){
int res=1;
for(;idx;idx>>=1,x=x*x%mod)
if(idx&1)
res=res*x%mod;
return res;
}
inline int inv(int x){
return ksm(x,mod-2);
}
inline int lag_range(){
int res=0;
for(int i=0;i<=n;++i){
int t=y[i],div=1;
for(int j=0;j<=n;++j){
if(i!=j){
t=t*(d-x[j]+mod)%mod;
div=div*(x[i]-x[j]+mod)%mod;
}
}
res=(res+t*inv(div)%mod)%mod;
}
return res;
}
inline void dfs(int x){
for(int i=1;i<=n;++i)
f[x][i]=1;
for(int y:G[x]){
dfs(y);
for(int j=1;j<=n;++j)
f[x][j]=f[x][j]*f[y][j]%mod;
}
for(int i=1;i<=n;++i)
f[x][i]=(f[x][i]+f[x][i-1])%mod;
}
signed main(){
cin>>n>>d;
for(int i=0;i<=n;++i) x[i]=i;
for(int i=2,fa;i<=n;++i){
cin>>fa;
G[fa].push_back(i);
}
dfs(1);
for(int i=1;i<=n;++i)
y[i]=f[1][i];
if(d<=n) cout<<f[1][d]<<endl;
else{
cout<<lag_range()<<endl;
}
}
P4463 calc#
我们设 表示前 个数使用的数值域为 的不同合法序列的值的和。
由于必须乱序,这里我们可以只用 序列中数递增的情况,最后总答案就会乘一个 ,也就变成了 。
式子就长这样:
显然这样 是会超时的,我们需要考虑优化。
我们设 (差分),则有:
若 可以用一个关于 的多项式来表达,则 也可以,因为对 求前缀和相当于给这个多项式的次数加一
考虑用数学归纳法算出 是一个次数为几的多项式。
- 当 时,有 次数为 。
- 每次 会求一次 的前缀和并给 乘上一个 ,次数相当于加了 。
- 所以说 就是一个关于 的 次多项式,而 就是一个关于 的 次多项式。
然后使用拉格朗日插值搞出 就完事了,时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N=505;
using ll=long long;
int n,m,k,p;
ll f[N][N*2],x[N*2],y[N*2];
inline int ksm(ll x,int idx){
ll res=1;
for(;idx;x=x*x%p,idx>>=1)
if(idx&1) res=res*x%p;
return res;
}
inline int inv(int x){return ksm(x,p-2);}
inline ll lag_range(int k){
if(k<=m)
return y[k];
ll res=0;
for(int i=1;i<=m;++i){
ll mul=y[i],div=1;
for(int j=1;j<=m;++j){
if(i==j) continue;
mul=mul*(k-x[j]+p)%p;
div=div*(x[i]-x[j]+p)%p;
}
res=(res+mul*inv(div)%p)%p;
}
return res;
}
signed main(){
cin>>k>>n>>p;
m=2*n+1;
ll frac=1;
for(int i=1;i<=n;++i)
frac=frac*i%p;
for(int i=0;i<=m;++i) f[0][i]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
f[i][j]=(f[i-1][j-1]*j%p+f[i][j-1])%p;
for(int i=1;i<=m;++i)
y[i]=f[n][i],x[i]=i;
printf("%d\n",lag_range(k)*frac%p);
}
#
拉格朗日插值优化 一般是建立在有关值域方面的 问题,面对这类问题我们通常先推导出暴力 式子,然后再想办法将 式子表示成关于某一个数的次数为 的多项式,最后通过求出 个点来插值。
若次数为 的话最少要使用 个点,但是也可以使用更多个点 (准没错),所以推不出来 却又猜出是拉格朗日插值的题可以多跑几个点,也就是可以根据数据范围来计算求几个点插值。
而且这种方式保险且能保证正确性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!