拉格朗日插值学习笔记

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

5.参考

OI-wiki
关于3.3的一个证明

posted @ 2020-12-11 23:36  ctt2006  阅读(182)  评论(0)    收藏  举报