【模板】拉格朗日插值

题目描述

小学知识可知,$n$个点$(x_i,y_i)$可以唯一地确定一个多项式

现在,给定$n$个点,请你确定这个多项式,并将$k$代入求值

求出的值对$998244353$取模

说明

$n \leq 2000 \; \; \; x_i,y_i,k \leq 998244353$

自为风月马前卒的分析

拉格朗日插值法

众所周知,\(n + 1\)个\(x\)坐标不同的点可以确定唯一的最高为\(n\)次的多项式。在算法竞赛中,我们常常会碰到一类题目,题目中直接或间接的给出了\(n+1\)个点,让我们求由这些点构成的多项式在某一位置的取值

一个最显然的思路就是直接高斯消元求出多项式的系数,但是这样做复杂度巨大\((n^3)\)且根据算法实现不同往往会存在精度问题

而拉格朗日插值法可以在\(n^2\)的复杂度内完美解决上述问题

假设该多项式为\(f(x)\), 第\(i\)个点的坐标为\((x_i, y_i)\),我们需要找到该多项式在\(k\)点的取值

根据拉格朗日插值法

\[f(k) = \sum_{i = 0}^{n} y_i \prod_{i \not = j} \frac{k - x[j]}{x[i] - x[j]}
\]

乍一看可能不是很好理解,我们来举个例子理解一下

假设给出的三个点为\((1, 3)(2, 7)(3, 13)\)

直接把\(f(k)展开\)

\(f(k) = 3 \frac{(k - 2)(k - 3)}{(1 - 2)(1 - 3)} + 7\frac{(k-1)(k-2)}{(2 - 1)(2-3)} + 13\frac{(k-1)(k-2)}{(3 -1)(3-2)}\)

观察不难得到,如果我们把\(x_i\)带入的话,除第\(i\)项外的每一项的分子中都会有\(x_i - x_i\),这样其他的所有项就都被消去了

因此拉格朗日插值法的正确性是可以保证的

下面说一下拉格朗日插值法的拓展

在\(x\)取值连续时的做法

在绝大多数题目中我们需要用到的\(x_i\)的取值都是连续的,这样的话我们可以把上面的算法优化到\(O(n)\)复杂度

首先把\(x_i\)换成\(i\),新的式子为

\(f(k) = \sum_{i=0}^n y_i \prod_{i \not = j} \frac{k - j}{i - j}\)

考虑如何快速计算\(\prod_{i \not = j} \frac{k - j}{i - j}\)

对于分子来说,我们维护出关于\(k\)的前缀积和后缀积,也就是

\[pre_i = \prod_{j = 0}^{i} k - j
\]

\[suf_i = \prod_{j = i}^n k - j
\]

对于分母来说,观察发现这其实就是阶乘的形式,我们用\(fac[i]\)来表示\(i!\)

那么式子就变成了

\[f(k) = \sum_{i=0}^n y_i \frac{pre_{i-1} * suf_{i+1}}{fac[i] * fac[N - i]}
\]

注意:分母可能会出现符号问题,也就是说,当\(N - i\)为奇数时,分母应该取负号

重心拉格朗日插值法

再来看一下前面的式子

\[f(k) = \sum_{i = 0}^{n} y_i \prod_{i \not = j} \frac{k - x[j]}{x[i] - x[j]}
\]

设\(g = \prod_{i=0}^n k - x[i]\)

\[f(k) = g\sum_{i = 0}^{n} \prod_{i \not = j} \frac{y_i}{(k - x[i])(x[i] - x[j])}
\]

设\(t_i = \frac{y_i}{\prod_{j \not =i} x_i - x_j}\)

\[f(k) = g\sum_{i = 0}^{n} \frac{t_i}{(k - x[i])}
\]

这样每次新加入一个点的时候只需要计算它的\(t_i\)即可

应用

首先讲一个经典应用:计算\(\sum_{i=1}^n i^k (n \leqslant 10^{15}, k \leqslant 10^6)\)

老祖宗告诉我们,这个东西是个以\(n\)为自变量的\(k + 1\)次多项式,具体证明可以看第二份参考资料

然后直接带入\(k+1\)个点后用拉格朗日插值算即可,复杂度\(O(k)\)

那具体在题目中怎么使用拉格朗日插值呢?

首先你要证明求的东西是某个多项式,判断的依据是:



大部分情况下归纳一下就可以了

时间复杂度\(O(n^2)\)

  1. int n,k,x[N],y[N];
  2. int main(){
  3. read(n),read(k);
  4. for(int i=1;i<=n;++i) read(x[i]),read(y[i]);
  5. int ans=0;
  6. for(int i=1;i<=n;++i){
  7. int val=1;
  8. for(int j=1;j<=n;++j)if(j!=i) val=mul(val,add(x[i],mod-x[j]));
  9. val=mul(y[i],fpow(val,mod-2));
  10. for(int j=1;j<=n;++j)if(j!=i) val=mul(val,add(k,mod-x[j]));
  11. ans=add(ans,val);
  12. }
  13. printf("%d\n",ans);
  14. return 0;
  15. }

成绩比较

有n个人m门学科,第i门的分数为不大于Ui的一个正整数

定义A「打爆」B当且仅当A的每门学科的分数都不低于B的该门学科的分数

已知第一个人第i们学科的排名为Ri,

即这门学科不低于n−Ri人的分数,但一定低于Ri−1人的分数

求有多少种方案使得第一个人恰好「打爆」了k个人

两种方案不同当且仅当存在两个人的分数不同

n,m≤100,Ui≤109

的题解

首先容斥

设\(g_x\)表示第一个人至少「打爆」了\(x\)个人的方案数,

\(A_i\)表示给所有人第\(i\)门学科分配分数使得第一个人排名正确的方案数,有

\[g_x=\binom{n-1}{x} \prod\limits_{i=1}^m \binom{n-x-1}{n-x-R_i}A_i
\]

\[A_i=\sum\limits_{j=1}^{U_i}j^{n-R_i}(U_i-j)^{R_i-1}
\]

\(g_x\)的意义是:先选出被吊打的\(x\)个人,再枚举每一门学科,这门学科比\(n-R_i\)人高。除去被吊打的\(x\)人外还需要在未被吊打的\(n-x-1\)人中选出\(n-R_i-x\)人这门比第一个人低,然后再给这\(n\)个人分配分数。

\(A_i\)的意义是:枚举第一个人第\(i\)门的分数\(j\),有\(n-R_i\)人分数不能高于\(j\),其余\(R_i-1\)人分数必须高于\(j\)。

容易发现瓶颈在快速计算$ A_i$上

我们将\(A_i\)二项式展开得

\[A_i=\sum\limits_{j=1}^{U_i}j^{n-R_i}(U_i-j)^{R_i-1}\\
=\sum\limits_{j=1}^{U_i}j^{n-R_i}\sum\limits_{k=0}^{R_i-1} \binom{R_i-1}{k}{(U_i)}^k(-j)^{R_i-k-1}\\
=\sum\limits_{k=0}^{R_i-1} \binom{R_i-1}{k}{(U_i)}^k\sum\limits_{j=1}^{U_i}j^{n-R_i}(-j)^{R_i-k-1}\\
=\sum\limits_{k=0}^{R_i-1} \binom{R_i-1}{k}{(U_i)}^k(-1)^{R_i-k-1}\sum\limits_{j=1}^{U_i}j^{n-k-1}
\]

前半部分非常好算,后半部分是一个自然数幂和,可以拉格朗日插值解决

拉格朗日插值过程中可以通过预处理前后缀的方式去掉不必要的求逆元复杂度使除预处理外单次\(O(n)\)。

这样就可以快速算出\(g_x\)了。

然后就是喜闻乐见的二项式反演环节

设\(f_x\)表示第一个人恰好「打爆」了$ x$个人

\[g_x=\sum\limits_{i=x}^n \binom{i}{x}f_i
\]

\[f_x=\sum\limits_{i=x}^n(-1)^{i-x} \binom{i}{x}g_i
\]

然后这道题就解决了,总复杂度:\(O(n^2m)\)

  1. co int N=100+10;
  2. int fac[N],ifac[N];
  3. int sp[N][N]; // the sum of the ith power of 1~j
  4. il int binom(int n,int m){
  5. return mul(fac[n],mul(ifac[m],ifac[n-m]));
  6. }
  7. int calc(int n,int k){
  8. if(!k) return n;
  9. if(n<N) return sp[k][n];
  10. static int pre[N],suf[N];
  11. pre[0]=1;
  12. for(int i=1;i<=k+2;++i) pre[i]=mul(pre[i-1],n-i);
  13. suf[k+3]=1;
  14. for(int i=k+2;i>=1;--i) suf[i]=mul(suf[i+1],n-i);
  15. int ans=0;
  16. for(int i=1;i<=k+2;++i){
  17. int down=mul(ifac[i-1],ifac[k+2-i]);
  18. if((k+2-i)&1) down=mod-down;
  19. ans=add(ans,mul(sp[k][i],mul(pre[i-1],mul(suf[i+1],down))));
  20. }
  21. return ans;
  22. }
  23. int U[N],R[N];
  24. int A[N],g[N];
  25. int main(){
  26. fac[0]=1;
  27. for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
  28. ifac[N-1]=fpow(fac[N-1],mod-2);
  29. for(int i=N-2;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
  30. for(int i=1;i<N;++i)
  31. for(int j=1;j<N;++j) sp[i][j]=add(sp[i][j-1],fpow(j,i));
  32. int n=read<int>(),m=read<int>(),K=read<int>();
  33. for(int i=1;i<=m;++i) read(U[i]);
  34. for(int i=1;i<=m;++i) read(R[i]);
  35. for(int i=1;i<=m;++i)
  36. for(int k=0;k<=R[i]-1;++k){
  37. int sum=mul(binom(R[i]-1,k),mul(fpow(U[i],k),calc(U[i],n-k-1)));
  38. A[i]=add(A[i],(R[i]-k-1)&1?mod-sum:sum);
  39. }
  40. for(int i=1;i<n;++i){
  41. g[i]=binom(n-1,i);
  42. for(int j=1;j<=m;++j){
  43. if(n-i-R[j]<0) {g[i]=0;break;}
  44. g[i]=mul(g[i],mul(binom(n-i-1,n-i-R[j]),A[j]));
  45. }
  46. }
  47. int ans=0;
  48. for(int i=K;i<=n;++i){
  49. int sum=mul(binom(i,K),g[i]);
  50. ans=add(ans,(i-K)&1?mod-sum:sum);
  51. }
  52. printf("%d\n",ans);
  53. return 0;
  54. }

LG4781 【模板】拉格朗日插值的更多相关文章

  1. CF622F——自然数幂和模板&&拉格朗日插值

    题意 求 $ \displaystyle \sum_{i=1}^n i^k \ mod (1e9+7), n \leq 10^9, k \leq 10^6$. CF622F 分析 易知答案是一个 $k ...

  2. LG4781 【模板】拉格朗日插值 和 JLOI2016 成绩比较

    [模板]拉格朗日插值 题目描述 由小学知识可知,$n$个点$(x_i,y_i)$可以唯一地确定一个多项式 现在,给定$n$个点,请你确定这个多项式,并将$k$代入求值 求出的值对$998244353$ ...

  3. 【Luogu4781】【模板】拉格朗日插值

    [Luogu4781][模板]拉格朗日插值 题面 洛谷 题解 套个公式就好 #include<cstdio> #define ll long long #define MOD 998244 ...

  4. P4781 【模板】拉格朗日插值

    P4781 [模板]拉格朗日插值 证明 :https://wenku.baidu.com/view/0f88088a172ded630b1cb6b4.html http://www.ebola.pro ...

  5. Luogu 4781 【模板】拉格朗日插值

    模板题. 拉格朗日插值的精髓在于这个公式 $$f(x) = \sum_{i = 1}^{n}y_i\prod _{j \neq i}\frac{x - x_i}{x_j - x_i}$$ 其中$(x_ ...

  6. luogu P4781 【模板】拉格朗日插值

    嘟嘟嘟 本来以为拉格朗日插值是一个很复杂的东西,今天学了一下才知道就是一个公式-- 我们都知道\(n\)个点\((x_i, y_i)\)可以确定唯一一个最高次为\(n - 1\)的多项式,那么现在我们 ...

  7. Luogu P4781【模板】拉格朗日插值

    洛谷传送门 板题-注意一下求多个数的乘积的逆元不要一个个快速幂求逆元,那样很慢,时间复杂度就是O(n2log)O(n^2log)O(n2log).直接先乘起来最后求一次逆元就行了.时间复杂度为O(nl ...

  8. fold算法(拉格朗日插值)

    如果打表发现某个数列: 差分有限次之后全为0 例如: 2017新疆乌鲁木齐ICPC现场赛D题 ,,,,,,,,,,…… [2018江苏南京ICPC现场赛也有这样的题目] 那么可以使用以下黑科技计算出第 ...

  9. 拉格朗日插值&&快速插值

    拉格朗日插值 插值真惨 众所周知$k+1$个点可以确定一个$k$次多项式,那么插值就是通过点值还原多项式的过程. 设给出的$k+1$个点分别是$(x_0,y_0),(x_1,y_1),...,(x_k ...

随机推荐

  1. SpringMVC中文乱码的解决办法

    中文乱码分类: (1)按照请求分类: GET请求乱码 POST请求乱码 (2)按照乱码位置分类 从前台传到后台的数据乱码(存储到数据库中的数据乱码) 从后台传到前台的数据乱码(显示在页面的数据乱码) ...

  2. jenkins使用jacoco插件检测代码覆盖率(八)

    代码覆盖率:类覆盖,方法覆盖,行覆盖,指令覆盖……(简而言之,就是判断有没有被执行) 覆盖率 = 已经执行的代码 / 总代码 (1)创建maven项目,配置pom.xml如下 pom.xml < ...

  3. 百度GIS API使用

    按照惯例,先来一段"Hello World"级别的直观的效果 实现效果: 实现代码: (WEB工程下直接运行即可) <!DOCTYPE html> <html&g ...

  4. SignalR NuGet程序包

    最近公司有一个边看直播边聊天的需求,直播好搞,直接用腾讯的小直播,组装推流和播放地址,把推流地址拿出去就OK,只要一推流,就可以使用播放地址观看直播,看完后通过webclient去异步下载直播的视频到 ...

  5. [Leetcode221]最大面积 Maximal Square

    [题目] Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's a ...

  6. Cracking The Coding Interview4.3

    //Given a sorted (increasing order) array, write an algorithm to create a binary tree with minimal h ...

  7. 插入排序算法 Java实现

    插入排序算法是算法排序中的一种: 该算法是假设已有序列是有序序列,从首元素(首元素为单个元素,肯定是有序的...)开始分析,对其他元素的位置进行有序的确定: 以算法为例: public class I ...

  8. intellij怎么导入MySQL的驱动包

    1.下载zip格式的驱动包:https://dev.mysql.com/downloads/connector/j/ 2.解压zip,放到任意位置.其中的mysql-connector-java.ja ...

  9. mysql增删改查练习

    Mysql增删改查sql语句练习 关于数据库的一些操作: 进入mysql 命令行: mysql -uroot –p 查看所有数据库: show databases; 创建数据库: create dat ...

  10. ​Web安全测试解决方案

    Web安全测试解决方案 介绍常见的Web安全风险,Web安全测试方法.测试基本理论和测试过程中的工具引入