拉格朗日插值可以出的很难,对于一个
n
n
n 阶多项式,可以优化成
O
(
n
log
n
)
O(n\log n)
O(nlogn) 但是需要
F
F
T
FFT
FFT 等知识点,博主比较菜就只能搞搞入门的了 (哭)
基础的高斯消元法可以
O
(
n
3
)
O(n^3)
O(n3) 求解
n
n
n 阶多项式,但是貌似不够用呀 这时就出现了
O
(
n
2
)
O(n^2)
O(n2) 的拉格朗日插值! 对于能求出
f
(
i
)
,
∀
i
∈
[
1
,
n
]
f(i),\forall i\in[1,n]
f(i),∀i∈[1,n],我们用 差分法 还可以化简到
O
(
n
)
O(n)
O(n)
引入
【题目描述】【模板】拉格朗日插值 | 洛谷 P4781 有
n
n
n 个不同点,给定他们的坐标
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi) 由于他们的横坐标都不相同,因此他们可以唯一确定一个
n
−
1
n-1
n−1 阶的多项式
P
(
x
)
P(x)
P(x) 给定
k
k
k ,你的任务是求出
P
(
k
)
P(k)
P(k) 取模
998244353
998244353
998244353
【数据范围】
1
≤
n
≤
2
×
1
0
3
1\le n\le 2\times10^3
1≤n≤2×103
1
≤
x
i
,
y
i
,
k
<
998244353
1\le x_i,y_i,k<998244353
1≤xi,yi,k<998244353
⌈
\lceil
⌈拉格朗日插值
⌋
\rfloor
⌋
我们知道多项式可以表示为
P
(
x
)
=
∑
a
i
x
i
P(x)=\sum a_ix^i
P(x)=∑aixi。 很神奇的,我们想构造出一个函数来表示这个
P
(
x
)
P(x)
P(x)
我们设一个函数:
l
i
(
x
)
=
∏
j
=
0
,
j
≠
i
n
x
−
x
j
x
i
−
x
j
l_i(x)=\prod_{j=0,j\ne i}^n\cfrac{x-x_j}{x_i-x_j}
li(x)=j=0,j=i∏nxi−xjx−xj 这个函数有什么性质呢?有一下两个性质: (1)
∀
i
∈
[
0
,
n
]
,
l
i
(
x
i
)
=
1
\forall i\in[0,n],l_i(x_i)=1
∀i∈[0,n],li(xi)=1 (2)
∀
i
,
j
∈
[
0
,
n
]
,
i
≠
j
,
l
i
(
x
j
)
=
0
\forall i,j\in[0,n],i\ne j,l_i(x_j)=0
∀i,j∈[0,n],i=j,li(xj)=0 接下来,我们就能直接构造出
P
(
x
)
P(x)
P(x)了:
P
(
x
)
=
∑
i
=
0
n
y
i
l
i
(
x
)
P(x)=\sum_{i=0}^n y_il_i(x)
P(x)=i=0∑nyili(x) 为啥这个多项式保证是正确的呢?因为我们有:
∀
i
∈
[
0
,
n
]
,
P
(
x
i
)
=
y
i
\forall i\in[0,n],P(x_i)=y_i
∀i∈[0,n],P(xi)=yi,保证成立。
代码
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
constint MAX =2e3+50;const ll MOD =998244353;
ll qpow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll inv(ll a){/* */returnqpow(a,MOD-2);}
ll X[MAX],Y[MAX];intmain(){int n;ll k;
cin >> n >> k;for(int i =0;i < n;++i){
cin >> X[i]>> Y[i];}
ll res =0;for(int i =0;i < n;++i){
ll num = Y[i];
ll den =1;for(int j =0;j < n;++j){if(i != j){
num = num *(k - X[j])% MOD;
den = den *(X[i]- X[j])% MOD;}}
num =(num + MOD)% MOD;
den =(den + MOD)% MOD;
res =(res + num % MOD *inv(den)% MOD)% MOD;}
cout << res;return0;}
【思路】 这下阶为
1
e
6
1e6
1e6了,
O
(
k
2
)
O(k^2)
O(k2) 已经不适用了。 我们如果算出前面一些
x
i
x_i
xi 和
y
i
y_i
yi,想快速算
l
i
(
n
)
l_i(n)
li(n),怎么办呢? 如果我们令
x
i
=
i
x_i=i
xi=i,那么式子会变成下面的样子:
l
i
(
n
)
=
∏
j
=
1
,
j
≠
i
k
n
−
j
i
−
j
l_i(n)=\prod_{j=1,j\ne i}^k\cfrac{n-j}{i-j}
li(n)=j=1,j=i∏ki−jn−j (1)对于分子,我们只要预处理出一个前缀积和后缀积,即可快速算。 令
p
r
e
(
i
)
=
∏
j
=
1
i
n
−
j
pre(i)=\prod_{j=1}^i n-j
pre(i)=∏j=1in−j,
s
u
f
(
i
)
=
∏
j
=
i
k
n
−
j
suf(i)=\prod_{j=i}^k n-j
suf(i)=∏j=ikn−j 那么分子就是
p
r
e
(
i
−
1
)
×
s
u
f
(
i
+
1
)
pre(i-1)\times suf(i+1)
pre(i−1)×suf(i+1) (2)对于分母,容易想到就是一个 断开的阶乘,就是
f
a
c
[
i
−
1
]
×
f
a
c
[
k
−
i
]
fac[i-1]\times fac[k-i]
fac[i−1]×fac[k−i] 不过要注意一下,因为当
j
>
i
j>i
j>i 时,后面每一项都为负数。若
k
−
i
k-i
k−i是一个奇数,那么后面会多一个负号。
P
(
n
)
=
∑
i
=
1
k
y
i
×
p
r
e
(
i
−
1
)
×
s
u
f
(
i
+
1
)
f
a
c
[
i
]
×
f
a
c
[
k
−
i
]
×
(
−
1
)
k
−
i
P(n)=\sum_{i=1}^ky_i\times\cfrac{pre(i-1)\times suf(i+1)}{fac[i]\times fac[k-i]}\times(-1)^{k-i}
P(n)=i=1∑kyi×fac[i]×fac[k−i]pre(i−1)×suf(i+1)×(−1)k−i 这下,时间复杂度变为线性的了!
代码
342
M
s
/
2000
M
s
342Ms/2000Ms
342Ms/2000Ms 时间复杂度:
O
(
k
)
O(k)
O(k)