拉格朗日插值学习笔记
0.前言
因为NOIP的T4所以才学习了这个算法
1.是什么?
P4781 【模板】拉格朗日插值
简单来说,就是给你\(n\)个点\((x_i,y_i)\),让你求出过这个点的不超过n-1次的函数取某一个数(下文记为\(x\))时的函数值
所以这在数列找规律时很有用(划掉)
2.怎么做(扑面而来的政治答题风)
原理很像CRT,就是你要同时满足n个"约束",你就先构造出满足其中一个,且对其他约束没有影响的式子,最后加起来即可
在这里,我们定义\(g_i(x)\)为过\((x_i,y_i)\),且在其它\(x_i\)处取值均为0的不超过n-1次的函数
显然\(g_i(x) = y_i\prod\limits_{j\ne i}\frac{x-x_j}{x_i-x_j}\),
则\(f(x) = \sum\limits_{i=1}^ng_i(x)\)
暴力计算即可
时间复杂度\(O(n^2)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
const int Mod = 998244353;
const int N = 2005;
int x[N],y[N],n,k;
int ans = 0;
inline int read() {
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int power(int x,int y) {
int res = 1;
for(;y;y >>= 1) {
if(y & 1) res = res * x%Mod;
x = x * x %Mod;
}
return res;
}
int calc(int t) {
int ret = y[t] % Mod;
for(int i = 1;i <= n;i++) {
if(i != t) {
ret = ret * (k-x[i]) % Mod * power(x[t]-x[i],Mod-2) % Mod;
}
}
return ret % Mod;
}
signed main () {
n = read();k = read();
for(int i = 1;i <= n;i++) {
x[i] = read();
y[i] = read();
}
for(int i = 1;i <= n;i++) {
ans = (ans + calc(i))%Mod;
ans = (ans + Mod) % Mod;
}
printf("%lld\n",ans);
return 0;
}
3.还能怎么做
3.1 字面意思(就是模板里给的那个东西)
3.2 可以用来求\(\sum\limits_{i=1}^ni^k\),预处理分子的前缀和后缀积,以及阶乘(你会发现分母就是两个阶乘的积)吗,可以做到\(O(n)\)
3.3 当DP的计算与每个状态本身的性质没关系时(这个我也不知道怎么理解,看题就懂了吧),有一维值域很大(记为K)时,这时我们可以尝试把每一个状态对应的值表示成一个多项式,利用DP方程推出它的次数(利用差分)(设为\(n\))。
然后那很大的一维只需处理到\(n+1\)即可,最后把终态的那\(n\)个状态拿出来插值,得到多项式为\(f(x)\),答案即为\(f(K)\)
tips:\(f(x,i)-f(x,i-1)\)的次数是\(f(x,i)\)的次数减一,要注意多项式相乘,次数是相加的,多项式相加,次数为最高次
4.例题
P4781 【模板】拉格朗日插值
CF622F The Sum of the k-th Powers
P4593 [TJOI2018]教科书般的亵渎
P4463 [集训队互测2012] calc
CF995F Cowmpany Cowmpensation

浙公网安备 33010602011771号