拉格朗日插值初步运用

前言

其实就是省选考了,然后我不会,正好记得去年NOIP好像也有要拉插的T4,所以就来学学。

这里默认会模板。

好像除了模板就没东西了...

还是讲一下吧qwq(其实就是OI-wiki的东西啦)

基础

  • 问题

给出 \(n\) 个点 \((x_i,y_i)\) ,将经过这 \(n\) 个点的 \(n-1\) 次的多项式记为 \(f(x)\),给定 \(k\) ,求 \(f(k)\) 的值。

  • 解法

说实话,记得之前第一次看拉插不是现在这种解法的。

之前没看过的建议去看原文章(也就是上面的链接去看看)学习。

看到最后就记住了这个公式:

\[f(x)=\sum_{i=1}^{n} y_i\prod_{j\neq i }\frac{x-x_j}{x_i-x_j} \]

所以拉插求出来的多项式并不是符合 \(a_0+a_1x+a_2x^2...\) 这样的,所以是不能方便的计算各项的系数的,但实际上我们的问题是只需要求 \(f(k)\) 的,所以我们就可以直接把 \(k\) 带入求值,得到:

\[f(k)=\sum_{i=1}^{n} y_i\prod_{j\neq i }\frac{k-x_j}{x_i-x_j} \]

  • 实现

实现中,对于一个 \(i\) ,我们可以把 \(x_i-x_j\) 先全乘起来,然后做一次快速幂,以达到 \(O(n^2)\) 的复杂度。

  • 特殊情况

当然,如果 \(\forall\ i,x_i=i\) ,式子就变成了:

\[f(k)=\sum_{i=1}^{n} y_i\prod_{j\neq i }\frac{k-j}{i-j} \]

发现分母变成了 \((-1)^{n-i}\times\frac{1}{(i-1)!(n-i)!}\),而分子是可以通过记录前缀积、后缀积优化的,所以这样我们是可以 \(O(n)\) 求出来的。

简单运用

话说还有什么复杂运用吗?

感觉还是要结合题目来说。

P7116 [NOIP2020] 微信步数

最开始我们还是要分析一下问题,并不是直接上拉插。

如果考虑对于每一个坐标取快速求其步数,这样是很低效率的,因为光需要枚举的坐标数就极大,所以我们要换一种思考方式。

考虑对每一步考虑有多少个坐标还能"存活",也就是没有走到过界外的起点个数,发现对于每一维可以分开考虑,会不合法的也就是左边一段和右边一段,所以我们需要记录 \(l_i,r_i\) 表示对第 \(i\) 维向左走最远、向右走最远的长度(换一种表示方法就是:假设这一维出发是坐标为 \(0\) ,当前第 \(i\) 维经过坐标的最小值,最大值为 \(l_i,r_i\)

所以答案就是 \(\sum\limits_{i=0}^{\infty}\prod\limits_{j=1}^k\max(0,w_i+r_{j}-l_j)\)

但是这个还是不好求,考虑分析 \(l_i,r_i\) 的变换时的特殊性质,我们记每 \(n\) 步为一轮。

可以发现只有第一轮是最特殊的,因为会遍历一堆没有经过的点,而在第二轮以及之后每一轮,两轮之间的相同步数之间 \(r_j-l_j\) 的差都是相等的,且为该维 \(n\) 步之后最后到达的坐标的绝对值,这个设为 \(d_i\)

形式化的说,设 \(L_{k,i,j},R_{k,i,j}\) 表示在第 \(k\) 轮,第 \(i\) 步的时候的 \(l_j,r_j\) 的值,那么对于 \(\forall \ k\ge 2,j,k,(R_{k+1,i,j}-L_{k+1,i,j})-(R_{k,i,j}-L_{k,i,j})=d_j\)

所以上面的式子就可以写成 \(\sum\limits_{x=0}^{\infty}\sum_\limits{i=1}^n\prod\limits_{j=1}^k\max(0,w_j+R_{2,i,j}-L_{2,i,j}-d_j(x-2))\)

我们求出 \(K=\min\{\frac{w_j+R_{1,n,j}-L_{1,n,j}}{d_j}\}\) ,因为 \(x<K\) 的时候后面都是不会小于 \(0\) 的,而 \(x>K\) 的时候对答案的贡献都是 \(0\) ,所以我们模拟一遍 \(x=K\) 那么就可以删去后面的 \(\max\) 了。

\(L_{2,i,j},R_{2,i,j}\) 都是可以在模拟一遍求出来的,所以相当于后面那一大坨我们都是可以知道具体的值的,而唯一会变换的就是 \(d_j(x-2)\) ,而这是一个关于 \(x\) 的一次多项式,而乘起来就是 \(k\) 次多项式,即我们可以设:

\[f(x)=\sum_\limits{i=1}^n\prod\limits_{j=1}^k(w_j+R_{2,i,j}-L_{2,i,j}-d_j(x-2)) \]

因为我们要求的是前缀和,所以就设

\[S(x)=\sum_{i=1}^xf(i) \]

需要注意,此时 \(S(x)\) 是一个 \(k+1\) 次多项式

为什么?

据 wyz 说,这可以参考自然数幂和的证明过程,就是差分之后才会变成 \(k\) 次多项式。

具体证明我也不会啊,反正感性理解就行了吧...

然后就可以拉插了。

怎么拉插?

因为我们可以求出 \(k+2\)\(S(x)\) 的值,也就是 \(S(2),S(3),...,S(k+3)\) ,然后我们可以假设我们求出的点值是 \((1,S(2)),(2,S(3)),...,(k+2,S(k+3))\) ,然后用拉插求出 \(x=K\) 的值就行了。

  • 为什么可以把点值看成 \((1,S(2))\),而不是 \((2,S(2))\)

    这其实是函数的平移,而且通过观察拉插的式子也知道我们其实并不关心 \(x_i\) 的具体值,而是两两之间的差。

复杂度是 \(O(nk^2)\) 的,注意要判 \(-1\) 的情况。

P8290 [省选联考 2022] 填树

首先要考虑暴力怎么写。

我们可以枚举最小值 \(L\),然后树上DP就可以求出答案。

怎么求啊?

首先可以按照 2022.2.25T1 的处理方法处理路径权值的 \(0,1\) 次方和。

所以就可以设 \(f_{x,0/1}\) 表示 x 为顶的链,其中值域全在枚举的 \([L,L+k]\) 中,其中否/是有 \(L\) 的方案数。

根据 \(l_i,r_i\)\([L,L+k]\) 来赋初值,之后就是普通树上DP。

所以这就是 \(O(n\text{值域})\) 的暴力做法。

如果你想了想初值应该怎么赋,那么你就会发现,对于 \(0\) 次方和的初值,要么是常数,要么就是 \(L+k-l_i\)\(r_i-L\) 这种一次函数,而随着树形DP的进行,只有加和乘也不影响其答案为多项式。

而这个多项式的次数最多就是 \(n\) 次( \(1\) 次方和的次数最多是 \(n+1\) 次,因为初始值是二次次函数,之后只会乘 \(0\) 次方和来合并。)

注意取前缀和次数要加一!

所以我们就可以拉插了。

注意,以上分析只适用于多项式各个系数都没有变化(也就是每个节点的多多项式都不发生变换的时候)所以,如果发生这几种情况的时候就要重新做一遍了:

  • 一个点的 \(l_i=L+k\) :左端点进入范围。
  • 一个点的 \(l_i=L+1\) : 左端点离开范围。
  • 一个点的 \(L+k=r_i+1\) : 右端点进入范围。
  • 一个点的 \(L=r_i+1\) : 右端点离开范围。

所以我们可以给每个点的这四种情况全部离散化之后按上面的拉插做法计算一次,因为这样的分界点有 \(O(n)\) 个,每次处理一遍要求 \(n+3\) 个点值,每次要 \(O(n)\) ,所以总复杂度就是 \(O(n^3)\) 的。

可以看出,在这题中,我们在拉插的时候带入的点值是 \((1,f(x)),(2,f(x+1)),...,(n+3,f(x+n+2))\) ,其中 \(x\) 表示分界点, \(f(x)\) 是树形DP最后求出来的答案,也展示了上面所说的 函数的平移 。

总结

可以看出,拉插其实就是一种优化暴力的方法,如果你知道你现在算的东西是一个关于 \(x\) 的多项式,且你要算 \(>10^9\) 次,那么就可以用拉插做到 \(O(\text{项数})\) 次。

当然,若你要计算的多项式还会少量变换,那么就离散化之后多算几次就行。

posted @ 2022-04-22 22:36  qwq_123  阅读(174)  评论(0编辑  收藏  举报