前言
这篇 b l o g blog blog 仅仅记录一下之前学的一些比较简单的东西,为了防止忘记。所以有不详细的地方请谅解QAQ。
拉格朗日插值
给出平面上的
n
+
1
n+1
n+1 个点
(
x
i
,
y
i
)
(
0
≤
i
≤
n
)
(x_i,y_i)(0\leq i\leq n)
(xi,yi)(0≤i≤n),找到一个
n
n
n 次多项式
f
(
x
)
f(x)
f(x),使得这个多项式过这
n
+
1
n+1
n+1 个点,给出一个
k
k
k,求
f
(
k
)
f(k)
f(k)。
n
≤
5000
n\leq 5000
n≤5000。
天才滴拉格朗日构造出了这个多项式(这里就不细究是怎么构造出来的了>_<):
f
(
k
)
=
∑
y
=
0
n
y
i
∏
i
≠
j
k
−
x
j
x
i
−
x
j
f(k)=\sum\limits_{y=0}^{n}y_i\prod\limits_{i\neq j}\dfrac{k-x_j}{x_i-x_j}
f(k)=y=0∑nyii=j∏xi−xjk−xj
容易验证
y
i
=
f
(
x
i
)
y_i=f(x_i)
yi=f(xi)(读者自证不难,而且
f
(
x
)
f(x)
f(x) 是关于
x
x
x 的
n
n
n 次多项式。
于是我们可以在
O
(
n
2
)
O(n^2)
O(n2) 复杂度完成插值。如果预处理出
∏
i
≠
j
1
x
i
−
x
j
\prod\limits_{i\neq j}\dfrac{1}{x_i-x_j}
i=j∏xi−xj1,就可以
O
(
n
)
O(n)
O(n) 完成插值!
拉格朗日插值的一个应用
求
1
k
+
2
k
+
.
.
.
+
n
k
1^k+2^k+...+n^k
1k+2k+...+nk。其中,
n
≤
1
0
15
,
k
≤
1
0
7
n\leq 10^{15},k\leq 10^7
n≤1015,k≤107。
组队赛出了这个我居然不会,太丢人了…
由小学知识,自然数
k
k
k 次幂的前缀和是一个
k
+
1
k+1
k+1 次多项式,于是可以通过前
k
+
2
k+2
k+2 个点插出这个多项式。
问题在于,怎么
O
(
k
)
O(k)
O(k) 求
f
(
x
)
=
∑
y
=
0
n
y
i
∏
i
≠
j
x
−
x
j
x
i
−
x
j
f(x)=\sum\limits_{y=0}^{n}y_i\prod\limits_{i\neq j}\dfrac{x-x_j}{x_i-x_j}
f(x)=y=0∑nyii=j∏xi−xjx−xj。
事实上,由于
x
x
x 坐标是连续的,我们可以将原式写成:
f
(
x
)
=
∑
y
=
0
n
y
i
∏
i
≠
j
x
−
j
i
−
j
f(x)=\sum\limits_{y=0}^{n}y_i\prod\limits_{i\neq j}\dfrac{x-j}{i-j}
f(x)=y=0∑nyii=j∏i−jx−j
然后呢,我们考虑优化连乘部分。
上面的
∏
i
≠
j
x
−
j
\prod\limits_{i\neq j}x-j
i=j∏x−j,可以写成
∏
j
=
0
k
+
1
x
−
j
x
−
i
\dfrac{\prod\limits_{j=0}^{k+1}x-j}{x-i}
x−ij=0∏k+1x−j,于是可以预处理。
下面的
∏
i
≠
j
i
−
j
\prod\limits_{i\neq j}i-j
i=j∏i−j,我们拆成两部分,变成
(
∏
j
=
0
i
−
1
(
i
−
j
)
)
×
(
∏
j
=
i
+
1
k
+
1
(
i
−
j
)
)
(\prod\limits_{j=0}^{i-1}(i-j))\times(\prod\limits_{j=i+1}^{k+1}(i-j))
(j=0∏i−1(i−j))×(j=i+1∏k+1(i−j)),前面那部分就是
i
!
i!
i!(这里是阶乘的意思,不是感叹号!),后面那部分就是
(
−
1
)
k
−
i
+
1
(
k
−
i
+
1
)
!
(-1)^{k-i+1}(k-i+1)!
(−1)k−i+1(k−i+1)!。
于是预处理阶乘和阶乘的逆,就可以实现
O
(
k
)
O(k)
O(k) 插值了!
点分治
点分治算法算是分治思想在树上的一个重大应用,不过其只能用于无根树QAQ。
点分治一般解决的是树上的路径问题。我们考虑当前的一棵子树,那么路径可以分为两种,一种是经过根节点的,一种是在他的子树中的。
第二种可以递归解决,如果可以在
O
(
子
树
大
小
)
O(子树大小)
O(子树大小) 的复杂度求出第一种的路径,那么我们就可以在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 总复杂度求解这个问题。(这里我讲的很不详细,建议读者自行做题理解>_<)
具体做法是每次求出子树的重心,以子树的重心为根解决子问题(由于这棵树是无根树,所以这种做法是可行的),不难证明,最多只会递归
l
o
g
n
logn
logn 层。
复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 的,详情可以参考主定理。
不过呢,我们可以考虑一种极端情况,就是满二叉树的情况。那么
T
(
n
)
=
2
T
(
n
2
)
+
n
T(n)=2T(\dfrac{n}{2})+n
T(n)=2T(2n)+n,可以看出复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 的。
可并堆
什么?你以为我要讲左偏树?那是不可能滴!因为我还没学
这里介绍的是一种黑科技,就是
p
b
d
s
pb_ds
pbds 库里面的配对堆,其用法非常简单,而且效率和左偏树差不多(稍慢一点)。
用法大概就是:
#include <ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
typedef __gnu_pbds::priority_queue<int, greater<int>, pairing_heap_tag> Heap;
常用操作:
push() //会返回一个迭代器
top() //同 stl
size() //同 stl
empty() //同 stl
clear() //同 stl
pop() //同 stl
join(priority_queue &other) //合并两个堆,other会被清空
split(Pred prd,priority_queue &other) //分离出两个堆
modify(point_iterator it,const key) //修改一个节点的值
一般左偏树能做的题都能用这个来做,除了部分需要可持久化的题目。
线段树合并
咕咕咕