拉格朗日插值
众所周知,我们有\(n+1\)个点值可以唯一确定一个\(n\)次的多项式。拉格朗日插值可以在\(O(n^2)\)的复杂度内求出这个多项式中任意一个\(f(k)\)的值。(当然你可以把\(k\)当成未知量\(x\)模拟多项式乘法和加法插出多项式的每个系数,复杂度\(O(n^3)\))
我们有这样一个多项式:
观察可以得到它满足当\(x=x_i\)时有\(f_j(x)=1\),而对于其他所有的\(x\)有\(f_j(x)=0\)。于是我们可以得出这\(n+1\)个点组成的多项式:
这个柿子的意义显然,当\(x=x_i\)时\(F(x)=y_i\),由此我们确定了一个多项式的点值表示。求这个多项式的点值表示可以模拟多项式乘法和加法,复杂度\(O(n^3)\)。当然你可以预处理\(x-x_j\)的前缀积然后每次扫到的时候做一个除法,复杂度\(O(n^2)\)。当然在模意义下你可以写个多项式求逆(就是麻烦点)。
于是我们就有了这个板子的解法。注意由于逆元很多,我们可以每次把分子分母分别保存然后统一处理逆元。
依题意模拟即可。
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=n;i++){
int up=y[i],down=1;
for(int j=1;j<=n;j++){
if(i==j)continue;
up=1ll*up*(k-x[j]+mod)%mod;
down=1ll*down*(x[i]-x[j]+mod)%mod;
}
ans=(ans+1ll*up*qpow(down,mod-2)%mod)%mod;
}
printf("%d",ans);
}
然后我们常用的要插出一个多项式的点值一般都可以预处理出前\(n+1\)个数的取值,所以我们有一种已知连续\(n+1\)个点值在\(O(n)\)的复杂度内求出\(f(k)\)的值的方法。(当然就算不连续你也可以\(O(n\log^2n)\)插出来)
首先还是看看原来的柿子:(假设\(x\)是有序的)
我们现在预处理了\(1-n+1\)的取值。把分子和分母分开考虑。关于分子,我们发现可以预处理\(x-x_j\)的前后缀积来处理。而分母由于取值连续,发现它是一个长得像阶乘的形式,即:
这个东西显然是\(O(n)\)的。来搞一个题。
int main(){
scanf("%d%d",&n,&k);
jc[0]=pre[0]=suf[k+3]=1;
for(int i=1;i<=k+2;i++){
jc[i]=1ll*jc[i-1]*i%mod;
pre[i]=1ll*pre[i-1]*(n-i+mod)%mod;//预处理前缀积
}
for(int i=k+2;i>=1;i--)suf[i]=1ll*suf[i+1]*(n-i+mod)%mod;//后缀积
int tmp=0,up,down;
for(int i=1;i<=k+2;i++){
tmp=(tmp+qpow(i,k))%mod;
up=1ll*pre[i-1]*suf[i+1]%mod;
down=1ll*jc[i-1]*jc[k+2-i]%mod*(((k-i)&1)?mod-1:1)%mod;
ans=(ans+1ll*qpow(down,mod-2)*up%mod*tmp%mod)%mod;
}//套柿子就行了
printf("%d",ans);
}
我写了个很退化的\(O(k\log k)\)写法。当然你可以线性筛+预处理阶乘逆元来达到\(O(k)\)。
然后是一个定理:\(k+1\)阶多项式的差分是\(k\)阶多项式。反之亦然。不会证明。