其实是复习,但基本上等同于学习(大雾)。
理论推导
基本公式
构造一个合法的函数 f ( x ) f(x) f(x),使得该函数过 n + 1 n+1 n+1个点 ( x i , y i ) (x_i,y_i) (xi,yi),且函数次数不超过 n n n。
我们先直接看这个构造出来的函数:
f
(
k
)
=
∑
i
=
1
n
y
i
∏
i
≠
j
k
−
x
j
x
i
−
x
j
f(k)=\sum^{n}_{i=1} y_i\prod_{i≠j}\frac{k-x_j}{x_i-x_j}
f(k)=i=1∑nyii=j∏xi−xjk−xj
将每个
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)带入公式就会发现这个函数神奇的含义:
- 若 k = x p k=x_p k=xp,那么对于 i ≠ p i≠p i=p,后面连乘的部分答案一定为 0 0 0, i = p i=p i=p时答案为 1 1 1。因此最终结果 f ( k ) = y p f(k)=y_p f(k)=yp
因此这个多项式完美的符合问题的要求,由于 n + 1 n+1 n+1一个点能够唯一确定一个 n n n次多项式,因此我们用构造的方法成功解决了这个问题。
重心拉格朗日插值
简单来说就是我们把式子变一下形,令
w
i
=
1
∏
i
≠
j
n
(
x
i
−
x
j
)
w_i=\frac{1}{\prod^n_{i≠j}(x_i-x_j)}
wi=∏i=jn(xi−xj)1
因此:
F
(
x
)
=
∏
i
=
0
n
(
x
−
x
i
)
×
∑
j
=
0
n
y
j
w
i
x
−
x
j
F(x)=\prod^n_{i=0}(x-x_i)\times \sum^{n}_{j=0}y_j\frac{w_i}{x-x_j}
F(x)=i=0∏n(x−xi)×j=0∑nyjx−xjwi
每次增加一个数时,我们每次将每个
w
i
w_i
wi多除以一个
(
x
i
−
x
n
+
1
)
(x_i-x_{n+1})
(xi−xn+1)即可。
这样动态增加数就是单次
O
(
n
)
O(n)
O(n)的了。
UPD:
在实际解决的问题中,我们很有可能遇到
x
x
x模意义下等于某一个
x
j
x_j
xj导致分子为零的情况。
因此我们定义
p
r
e
i
=
∏
j
=
0
i
−
1
(
x
−
x
j
)
,
s
u
f
i
=
∏
j
=
i
+
1
n
(
x
−
x
j
)
pre_i=\prod^{i-1}_{j=0}(x-x_j),suf_i=\prod^{n}_{j=i+1}(x-x_j)
prei=∏j=0i−1(x−xj),sufi=∏j=i+1n(x−xj)。
原式变为
F
(
x
)
=
∑
j
=
0
n
y
j
⋅
p
r
e
i
⋅
s
u
f
i
⋅
w
i
F(x)=\sum^n_{j=0}y_j·pre_i·suf_i·w_i
F(x)=j=0∑nyj⋅prei⋅sufi⋅wi
x x x取值连续时的拉格朗日插值I
给定一个不超过 n n n 次的多项式的 n + 1 n+1 n+1 个点值 f ( 0 ) , f ( 1 ) … f ( n ) f(0),f(1) \dots f(n) f(0),f(1)…f(n),和一个正整数 m m m,求 f ( m ) f(m) f(m)
事实上,多数题目的
x
x
x取值都可以是连续的,此时我们可以利用一些特性来优化插值的过程。
例如上面的问题,我们代换一下公式:
f
(
x
)
=
∑
i
=
0
n
y
i
∏
i
≠
j
x
−
j
i
−
j
f(x)=\sum^n_{i=0}y_i \prod_{i≠j}\frac{x-j}{i-j}
f(x)=i=0∑nyii=j∏i−jx−j
除式下面的部分可以观察得到
(
−
1
)
m
−
i
i
!
(
m
−
i
)
!
(-1)^{m-i}i!(m-i)!
(−1)m−ii!(m−i)!,上面的部分显然可以使用阶乘表示,得到:
f
(
x
)
=
x
!
(
x
−
n
−
1
)
!
∑
i
=
0
n
(
−
1
)
n
−
i
y
i
i
!
(
n
−
i
)
!
×
1
x
−
j
f(x)=\frac{x!}{(x-n-1)!}\sum^n_{i=0}\frac{(-1)^{n-i}y_i}{i!(n-i)!}\times \frac{1}{x-j}
f(x)=(x−n−1)!x!i=0∑ni!(n−i)!(−1)n−iyi×x−j1
UPD: 如果需要处理分母为零的情况,用重心拉个朗日插值的解决方法同样解决即可。
x x x取值连续时的拉格朗日插值II
给定一个不超过 n n n 次的多项式的 n + 1 n+1 n+1 个点值 f ( 0 ) , f ( 1 ) … f ( n ) f(0),f(1) \dots f(n) f(0),f(1)…f(n),和一个正整数 m m m,求 f ( m ) , f ( m + 1 ) , … f ( m + n ) f(m),f(m+1),\dots f(m+n) f(m),f(m+1),…f(m+n)。
其实想法挺巧妙的,我好像没见过这个套路。
由于
x
x
x连续,我们套用上面的公式:
f
(
x
+
k
)
=
(
x
+
k
)
!
(
x
+
k
−
n
−
1
)
!
∑
i
=
0
n
(
−
1
)
n
−
i
y
i
i
!
(
n
−
i
)
!
×
1
x
+
k
−
j
f(x+k)=\frac{(x+k)!}{(x+k-n-1)!}\sum^n_{i=0}\frac{(-1)^{n-i}y_i}{i!(n-i)!}\times \frac{1}{x+k-j}
f(x+k)=(x+k−n−1)!(x+k)!i=0∑ni!(n−i)!(−1)n−iyi×x+k−j1
看起来很像卷积:
A
(
x
)
=
∑
i
=
0
n
(
−
1
)
n
−
i
y
i
i
!
(
n
−
i
)
!
,
B
(
x
)
=
∑
i
=
0
n
1
x
+
k
−
j
A(x)=\sum^n_{i=0}\frac{(-1)^{n-i}y_i}{i!(n-i)!},B(x)=\sum^n_{i=0}\frac{1}{x+k-j}
A(x)=i=0∑ni!(n−i)!(−1)n−iyi,B(x)=i=0∑nx+k−j1
然而
B
(
x
)
B(x)
B(x)里面有
k
k
k,不能直接做。
但是注意到相邻两个点值表示的多项式等价于每次向左平移一位。
因此我们重新定义
B
(
x
)
=
∑
i
=
0
2
n
1
x
+
i
−
n
B(x)=\sum^{2n}_{i=0}\frac{1}{x+i-n}
B(x)=i=0∑2nx+i−n1
此时
A
(
x
)
⋅
B
(
x
)
A(x)·B(x)
A(x)⋅B(x)的第
[
n
,
2
n
]
[n,2n]
[n,2n]项分别对应的就是
f
(
x
+
k
)
f(x+k)
f(x+k)。
画一下图就知道为什么了。
实现细节:因为 m m m有一点大所以预处理阶乘很麻烦…
例题
拉格朗日插值法的最大难点其实在于对多项式次数的分析。
然而实践证明多拿几个点来插值也没有什么问题。
P4463 [集训队互测2012] calc
离散拉格朗日插值基础dp题。
定义dp_{i,j}表示
i
i
i个数,每个数在
[
1
,
j
]
[1,j]
[1,j]的范围内的方案数。
首先写出dp式,
f
i
,
j
=
f
i
−
1
,
j
−
1
×
i
×
j
+
f
i
,
j
−
1
f_{i,j}=f_{i-1,j-1}\times i\times j+f_{i,j-1}
fi,j=fi−1,j−1×i×j+fi,j−1。
然后我们证明
f
(
i
)
f(i)
f(i)是一个
2
i
2i
2i次的多项式。
设
f
(
i
)
f(i)
f(i)多项式次数为
g
i
g_i
gi。
移项
f
i
,
j
−
f
i
,
j
−
1
=
f
i
−
1
,
j
−
1
×
i
×
j
f_{i,j}-f_{i,j-1}=f_{i-1,j-1}\times i\times j
fi,j−fi,j−1=fi−1,j−1×i×j
左边是次数是
g
i
−
1
g_i-1
gi−1的[
x
d
−
(
x
−
1
)
d
x^d-(x-1)^d
xd−(x−1)d没有
x
d
x^d
xd项]。
因此
g
i
−
1
=
g
i
−
1
+
1
g_i-1=g_{i-1}+1
gi−1=gi−1+1.
g
i
=
2
i
g_i=2i
gi=2i
然后带入
2
n
+
1
2n+1
2n+1(包括i=0)个点插值即可。
复杂度:
O
(
n
2
)
O(n^2)
O(n2)。
CF995F Cowmpany Cowmpensation
跟上一题没什么区别,写出dp式子然后分析多项式次数,同样插值即可。
复杂度:
O
(
n
2
)
O(n^2)
O(n2)。
[JLOI2016]成绩比较
我们可以尝试着用
d
p
dp
dp来解决这个问题。
定义
f
i
,
j
f_{i,j}
fi,j表示已经处理了前
i
i
i次考试成绩,现在有
j
j
j个人被B神碾压的方案数。
f
i
,
j
=
(
∑
k
=
j
n
f
i
−
1
,
k
⋅
(
k
j
)
⋅
(
n
−
k
−
1
r
i
−
1
−
(
k
−
j
)
)
)
∑
k
=
1
u
i
k
n
−
r
i
⋅
(
u
i
−
k
)
r
i
−
1
f_{i,j}=\bigg( \sum^n_{k=j} f_{i-1,k}·\binom{k}{j}·\binom{n-k-1}{r_i-1-(k-j)} \bigg)\sum^{u_i}_{k=1} k^{n-r_i}·(u_i-k)^{r_i-1}
fi,j=(k=j∑nfi−1,k⋅(jk)⋅(ri−1−(k−j)n−k−1))k=1∑uikn−ri⋅(ui−k)ri−1
看上去一切顺利,但是
u
i
u_i
ui的范围是
1
0
9
10^9
109,直接处理显然会T。
我们设这个部分
f
(
i
)
=
∑
k
=
1
u
i
k
n
−
r
i
⋅
(
u
i
−
k
)
r
i
−
1
f(i)=\sum^{u_i}_{k=1} k^{n-r_i}·(u_i-k)^{r_i-1}
f(i)=∑k=1uikn−ri⋅(ui−k)ri−1。
和式里面是一个
n
−
1
n-1
n−1次的多项式(关于
u
i
u_i
ui)。
那么求和就变成了一个
n
n
n次多项式。
插值求出每个
f
(
i
)
f(i)
f(i)即可。
复杂度:
O
(
n
3
log
n
)
O(n^3\log n)
O(n3logn)(搞了个快速幂…)。
[2018 集训队互测]小 H 爱染色
[NOI2019] 机器人
这种题目口胡起来还是很愉快的…
首先我们很容易得到一个
50
50
50分的dp。
定义
d
p
[
l
]
[
r
]
[
v
]
dp[l][r][v]
dp[l][r][v]为
[
l
,
r
]
[l,r]
[l,r]中最大值不差过
v
v
v的方案数。
那么有
d
p
[
l
]
[
r
]
[
v
]
=
∑
m
i
d
∑
k
d
p
[
l
]
[
m
i
d
−
1
]
[
k
]
×
d
p
[
m
i
d
+
1
]
[
r
]
[
k
−
1
]
dp[l][r][v]=\sum_{mid}\sum_{k}dp[l][mid-1][k]\times dp[mid+1][r][k-1]
dp[l][r][v]=∑mid∑kdp[l][mid−1][k]×dp[mid+1][r][k−1]。
然后有一个结论:将给出的区间一起离散化,每相邻两点的一个区间是一个
n
n
n次的多项式。
有了这个结论了之后我们对每个段只取前
n
n
n个值进行dp,接下来的我们可以直接通过插值插出来。
这样可以得到95分的好成绩。
如果要得到满分的话我们需要将每层一定用不到的dp值域舍去(因为对于区间比较短的dp数组,
k
k
k比较大的状态其实是和
d
p
[
1
]
[
n
]
[
p
−
p
+
n
−
1
]
dp[1][n][p-p+n-1]
dp[1][n][p−p+n−1]无关的)。