【模板】拉格朗日插值

题目描述

小学知识可知,$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)\)

int n,k,x[N],y[N];
int main(){
read(n),read(k);
for(int i=1;i<=n;++i) read(x[i]),read(y[i]);
int ans=0;
for(int i=1;i<=n;++i){
int val=1;
for(int j=1;j<=n;++j)if(j!=i) val=mul(val,add(x[i],mod-x[j]));
val=mul(y[i],fpow(val,mod-2));
for(int j=1;j<=n;++j)if(j!=i) val=mul(val,add(k,mod-x[j]));
ans=add(ans,val);
}
printf("%d\n",ans);
return 0;
}

成绩比较

有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)\)

co int N=100+10;
int fac[N],ifac[N];
int sp[N][N]; // the sum of the ith power of 1~j il int binom(int n,int m){
return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int calc(int n,int k){
if(!k) return n;
if(n<N) return sp[k][n]; static int pre[N],suf[N];
pre[0]=1;
for(int i=1;i<=k+2;++i) pre[i]=mul(pre[i-1],n-i);
suf[k+3]=1;
for(int i=k+2;i>=1;--i) suf[i]=mul(suf[i+1],n-i); int ans=0;
for(int i=1;i<=k+2;++i){
int down=mul(ifac[i-1],ifac[k+2-i]);
if((k+2-i)&1) down=mod-down;
ans=add(ans,mul(sp[k][i],mul(pre[i-1],mul(suf[i+1],down))));
}
return ans;
} int U[N],R[N];
int A[N],g[N];
int main(){
fac[0]=1;
for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
ifac[N-1]=fpow(fac[N-1],mod-2);
for(int i=N-2;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
for(int i=1;i<N;++i)
for(int j=1;j<N;++j) sp[i][j]=add(sp[i][j-1],fpow(j,i)); int n=read<int>(),m=read<int>(),K=read<int>();
for(int i=1;i<=m;++i) read(U[i]);
for(int i=1;i<=m;++i) read(R[i]); for(int i=1;i<=m;++i)
for(int k=0;k<=R[i]-1;++k){
int sum=mul(binom(R[i]-1,k),mul(fpow(U[i],k),calc(U[i],n-k-1)));
A[i]=add(A[i],(R[i]-k-1)&1?mod-sum:sum);
}
for(int i=1;i<n;++i){
g[i]=binom(n-1,i);
for(int j=1;j<=m;++j){
if(n-i-R[j]<0) {g[i]=0;break;}
g[i]=mul(g[i],mul(binom(n-i-1,n-i-R[j]),A[j]));
}
} int ans=0;
for(int i=K;i<=n;++i){
int sum=mul(binom(i,K),g[i]);
ans=add(ans,(i-K)&1?mod-sum:sum);
}
printf("%d\n",ans);
return 0;
}

LG4781 【模板】拉格朗日插值 和 JLOI2016 成绩比较的更多相关文章

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

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

  2. LG4781 【模板】拉格朗日插值

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

  3. bzoj千题计划270:bzoj4559: [JLoi2016]成绩比较(拉格朗日插值)

    http://www.lydsy.com/JudgeOnline/problem.php?id=4559 f[i][j] 表示前i门课,有j个人没有被碾压的方案数 g[i] 表示第i门课,满足B神排名 ...

  4. BZOJ4599[JLoi2016&LNoi2016]成绩比较(dp+拉格朗日插值)

    这个题我们首先可以dp,f[i][j]表示前i个科目恰好碾压了j个人的方案数,然后进行转移.我们先不考虑每个人的分数,先只关心和B的相对大小关系.我们设R[i]为第i科比B分数少的人数,则有f[i][ ...

  5. bzoj 4559 [JLoi2016]成绩比较 —— DP+拉格朗日插值

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 看了看拉格朗日插值:http://www.cnblogs.com/ECJTUACM-8 ...

  6. P3270 [JLOI2016]成绩比较 容斥 数论 组合数学 拉格朗日插值

    LINK:成绩比较 大体思路不再赘述 这里只说几个我犯错的地方. 拉格朗日插值的时候 明明是n次多项式 我只带了n个值进去 导致一直GG. 拉格朗日插值的时候 由于是从1开始的 所以分母是\((i-1 ...

  7. BZOJ.4559.[JLOI2016]成绩比较(DP/容斥 拉格朗日插值)

    BZOJ 洛谷 为什么已经9点了...我写了多久... 求方案数,考虑DP... \(f[i][j]\)表示到第\(i\)门课,还有\(j\)人会被碾压的方案数. 那么\[f[i][j]=\sum_{ ...

  8. 【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

    [题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一 ...

  9. bzoj 4559 [JLoi2016]成绩比较——拉格朗日插值

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 关于拉格朗日插值,可以看这些博客: https://www.cnblogs.com/E ...

随机推荐

  1. (CSDN迁移) html中的rel属性

    在页面上若需要同时存在多个页面,就通过一个button开启一个页面时,就需要指定不同的 rel="relName". 否则新页面就会将就原有的页面给覆盖掉.

  2. Linux中使用MegaCli工具查看、管理Raid卡信息

    MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态,等等.通常,我们对硬盘当前的状态不太好确定, ...

  3. 微信小程序文档

    提示框: wx.showToast(OBJECT) 显示消息提示框 OBJECT参数说明: 示例代码: wx.showToast({ title: '成功', icon: 'success', dur ...

  4. 如何在一个Docker中同时运行多个程序进程?

    我们都知道Docker容器的哲学是一个Docker容器只运行一个进程,但是有时候我们就是需要在一个Docker容器中运行多个进程 那么基本思路是在Dockerfile 的CMD 或者 ENTRYPOI ...

  5. Mycat使用--分库分表和读写分离

    Mycat分库分表读写分离 1. 模拟多数据库节点 2. 配置文件 具体操作参看: https://blog.csdn.net/vbirdbest/article/details/83448757 写 ...

  6. Windows查看端口使用状况(转)

    转:https://www.cnblogs.com/lixuwu/p/5898354.html 阅读目录 1 查看windows所有端口进程 2 查询指定端口 使用端口是我们在进行远程或者打印机等都会 ...

  7. 一些常用的 Emoji 符号(可直接复制)

    表情类

  8. docker实战 (3) 常规配置

    本节会持续更新,在项目实战中遇到的docker配置都会更新进来 docker常用命令: docker 介绍: what: 是什么 why: 为什么用 how: 怎么用 docker 特点: 轻量级,可 ...

  9. scrapy 爬虫中间件-offsite和refer中间件

    环境使用anaconda 创建的pyithon3.6环境 mac下 source activate python36 mac@macdeMacBook-Pro:~$ source activate p ...

  10. PHP Lumen Laravel 解决validate方法自定义message无效的问题

    /** * 由于 \Laravel\Lumen\Routing\ProvidesConvenienceMethods::validate 在验证不通过时, * 抛出 \Illuminate\Valid ...