多项式的求逆、取模和多点求值学习小记
最近学习了多项式的求逆、取模和多点求值,这些方法能够解决很多多项式问题。
这三个操作是环环相扣的,很有趣,学完后不妨记录一下。
多项式求逆
给出一个次数界为 nnn 的多项式 A(x)A(x)A(x) ,需要求 B(x)B(x)B(x) 满足: A(x)B(x)≡1(modxn)A(x)B(x)\equiv1(mod\ x^n)A(x)B(x)≡1(mod xn)
我们考虑倍增来求解,假设我们已经知道 G(x)G(x)G(x) 满足:A(x)G(x)≡1(modx⌊n2⌋)A(x)G(x)\equiv1(mod\ x^{\lfloor\frac{n}{2}\rfloor})A(x)G(x)≡1(mod x⌊2n⌋)
那么开始推导,两式相减:B(x)−G(x)≡0(modx⌊n2⌋)B(x)-G(x)\equiv0(mod\ x^{\lfloor\frac{n}{2}\rfloor})B(x)−G(x)≡0(mod x⌊2n⌋)
两边平方:B(x)2+G(x)2−2G(x)B(x)≡0(modxn)B(x)^2+G(x)^2-2G(x)B(x)\equiv0(mod\ x^n)B(x)2+G(x)2−2G(x)B(x)≡0(mod xn)
两边同乘 A(x)A(x)A(x) :B(x)+A(x)G(x)2−2G(x)≡0(modxn)B(x)+A(x)G(x)^2-2G(x)\equiv0(mod\ x^n)B(x)+A(x)G(x)2−2G(x)≡0(mod xn)
于是可求得 B(x)B(x)B(x) :B(x)≡2G(x)−A(x)G(x)2(modxn)B(x)\equiv2G(x)-A(x)G(x)^2(mod\ x^n)B(x)≡2G(x)−A(x)G(x)2(mod xn)
当递归至 n=1n=1n=1 时则有:B(0)=A(0)−1B(0)=A(0)^{-1}B(0)=A(0)−1
那么我们开始时就次数界 nnn 配成 2k2^k2k 的形式,直接做倍增即可完成求逆。
时间复杂度 O(nlogn)O(n\ log\ n)O(n log n) 。
模板题:洛谷 P4238 【模板】多项式求逆
多项式取模
多项式取模是依赖于多项式求逆的。
给出一个次数为 nnn 的多项式 A(x)A(x)A(x) ,一个次数为 m(m≤n)m(m\leq n)m(m≤n) 的多项式 B(x)B(x)B(x)。
我们需要求出多项式 C(x)C(x)C(x) 和 R(x)R(x)R(x) 满足:A(x)=B(x)C(x)+R(x)(∗)A(x)=B(x)C(x)+R(x)\ \ \ \ \ \ (*)A(x)=B(x)C(x)+R(x) (∗)
其中 C(x)C(x)C(x) 的次数 ≤n−m\leq n-m≤n−m ,R(x)R(x)R(x) 的次数 <m<m<m 。
考虑一个翻转操作:AR(x)=xnA(1x)A^R(x)=x^nA(\frac{1}{x})AR(x)=xnA(x1)
其实质就是多项式的系数翻转过来。
将 (∗)(*)(∗) 式两边同乘 xnx^nxn 、并令 x=1xx=\frac{1}{x}x=x1 代入,可得:xnA(1x)=xmB(1x)⋅xn−mC(1x)+xn−m+1xm−1R(1x)x^nA(\frac{1}{x})=x^mB(\frac{1}{x})·x^{n-m}C(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x})xnA(x1)=xmB(x1)⋅xn−mC(x1)+xn−m+1xm−1R(x1)
则有:AR(x)=BR(x)CR(x)+xn−m+1RR(x)A^R(x)=B^R(x)C^R(x)+x^{n-m+1}R^R(x)AR(x)=BR(x)CR(x)+xn−m+1RR(x)
关键一步来了,把上式放在 (modxn−m+1)(mod\ x^{n-m+1})(mod xn−m+1) 下,就能消除 RR(x)R^R(x)RR(x) 的影响(并且对 CR(x)C^R(x)CR(x) 无影响,其每一项次数都 ≤n−m\leq n-m≤n−m):AR(x)≡BR(x)CR(x)(modxn−m+1)A^R(x)\equiv B^R(x)C^R(x)\ \ (mod\ x^{n-m+1})AR(x)≡BR(x)CR(x) (mod xn−m+1)
于是我们通过多项式求逆可以算出 CR(x)C^R(x)CR(x) :CR(x)≡AR(x)BR(x)(modxn−m+1)C^R(x)\equiv \frac{A^R(x)}{B^R(x)}\ \ (mod\ x^{n-m+1})CR(x)≡BR(x)AR(x) (mod xn−m+1)
之后翻转得到 C(x)C(x)C(x) ,回代 (∗)(*)(∗) 式求出 R(x)R(x)R(x) 即可!
以上各操作均是 O(nlogn)O(n\ log \ n)O(n log n) 的。
模板题:洛谷 P4512 【模板】多项式除法
多项式多点求值
多点求值需要用到多项式取模!
给出一个 nnn 次多项式 F(x)F(x)F(x) ,和 mmm 个值 x1,x2,⋅⋅⋅,xmx_1,x_2,···,x_mx1,x2,⋅⋅⋅,xm ,要求 F(x1),F(x2),⋅⋅⋅,F(xm)F(x_1),F(x_2),···,F(x_m)F(x1),F(x2),⋅⋅⋅,F(xm) 。
比较关键的一个性质:F(x)mod(x−a)=F(a)F(x)\ mod\ (x-a)=F(a)F(x) mod (x−a)=F(a)
用因式定理或者直接将 (x−a+a)k(x-a+a)^k(x−a+a)k 二项式展开都能很容易得到。
那么我们依据这个来分治解决本问题。
令 L(x)=∑i=1⌊n2⌋(x−xi)L(x)=\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}(x-x_i)L(x)=∑i=1⌊2n⌋(x−xi) ,R(x)=∑i=⌊n2⌋+1n(x−xi)R(x)=\sum_{i=\lfloor\frac{n}{2}\rfloor+1}^{n}(x-x_i)R(x)=∑i=⌊2n⌋+1n(x−xi) ,
那么对于 1≤i≤⌊n2⌋1\leq i\leq\lfloor\frac{n}{2}\rfloor1≤i≤⌊2n⌋ ,F(xi)=(FmodL)(xi)F(x_i)=(F\ mod\ L)(x_i)F(xi)=(F mod L)(xi) ;对于 ⌊n2⌋+1≤i≤n\lfloor\frac{n}{2}\rfloor+1\leq i\leq n⌊2n⌋+1≤i≤n ,F(xi)=(FmodR)(xi)F(x_i)=(F\ mod\ R)(x_i)F(xi)=(F mod R)(xi)。(原理同上述性质)
于是我们向线段树一样往下分治计算即可。
具体来说就是先预处理出每个区间的 ∏i=lr(x−xi)\prod_{i=l}^r(x-x_i)∏i=lr(x−xi) (相当于是求出 L,RL,RL,R ,自下而上),
之后再自上而下地把 F(x)F(x)F(x) 往下传,每次就模 LLL 或 RRR 并分别传向左右子区间。
当分治到底层(l=rl=rl=r)的时候,F(x)F(x)F(x) 就是被模剩一个常数项 F′(0)F'(0)F′(0) 了,直接就有 F(xi)=F′(0)F(x_i)=F'(0)F(xi)=F′(0) 。
每次做多项式取模是 O(nlogn)O(n\ log\ n)O(n log n) 的,套上分治就是 O(nlog2n)O(n\ log^2n)O(n log2n) 的了。当然常数很大……
模板题:洛谷 P5050 【模板】多项式多点求值
这里给出这题的代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=64005,M=16,G=3,mo=998244353;
int tot;
int f[N],p[N],ans[N];
int mul[N*M<<1],st[N<<2],en[N<<2];
int g[N*M<<1],stg[N<<2],eng[N<<2];
int a[N],b[N],c[N],rr[N];//a=b*c+rr
int ra[N],rb[N<<1],irb[N<<2];
int f1[N<<2],f2[N<<2],rev[N<<2],wn[N<<2];
inline int read()
{int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X;
}
void write(int x)
{if(x>9) write(x/10);putchar(x%10+'0');
}
inline int ksm(int x,int y)
{int s=1;while(y){if(y&1) s=(LL)s*x%mo;x=(LL)x*x%mo;y>>=1;}return s;
}
inline void NTT(int *y,int len,int ff)
{for(int i=0;i<len;i++)if(i<rev[i]) swap(y[i],y[rev[i]]);for(int h=2,d=len>>1;h<=len;h<<=1,d>>=1)for(int i=0,k=h>>1;i<len;i+=h)for(int j=0,cnt=0;j<k;j++,cnt+=d){int u=y[i+j],t=(LL)wn[cnt]*y[i+j+k]%mo;y[i+j]=u+t>=mo?u+t-mo:u+t;y[i+j+k]=u-t<0?u-t+mo:u-t;}if(ff==-1){for(int i=len>>1;i;i--) swap(y[i],y[len-i]);int inv=ksm(len,mo-2);for(int i=0;i<len;i++) y[i]=(LL)y[i]*inv%mo;}
}
void solve(int len,int num)
{if(len==1){irb[0]=ksm(rb[0],mo-2);return;}solve(len>>1,num-1);for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<num-1;int w0=ksm(G,(mo-1)/len);for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;for(int i=0;i<len>>1;i++) f1[i]=rb[i];NTT(f1,len,1),NTT(irb,len,1);for(int i=0;i<len;i++) f1[i]=(2-(LL)f1[i]*irb[i]%mo+mo)*irb[i]%mo;NTT(f1,len,-1);for(int i=0;i<len>>1;i++) irb[i]=f1[i];for(int i=len>>1;i<len;i++) irb[i]=0;
}
void make(int v,int l,int r)
{if(l==r){st[v]=++tot;mul[tot]=mo-p[l];en[v]=++tot;mul[tot]=1;return;}int mid=l+r>>1,ls=v<<1,rs=ls|1;make(ls,l,mid);make(rs,mid+1,r);int na=en[ls]-st[ls]+1,nb=en[rs]-st[rs]+1;int len=1,ll=0;while(len<na+nb) len<<=1,ll++;for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;int w0=ksm(G,(mo-1)/len);for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;for(int i=0;i<na;i++) f1[i]=mul[st[ls]+i];for(int i=na;i<len;i++) f1[i]=0;for(int i=0;i<nb;i++) f2[i]=mul[st[rs]+i];for(int i=nb;i<len;i++) f2[i]=0;NTT(f1,len,1),NTT(f2,len,1);for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;NTT(f1,len,-1);na+=nb;while(na>1 && !f1[na-1]) na--;st[v]=tot+1;for(int i=0;i<na;i++) mul[++tot]=f1[i];en[v]=tot;
}
void find(int v,int l,int r,int fa)
{int na=eng[fa]-stg[fa],nb=en[v]-st[v];if(na>=nb){for(int i=0;i<=na;i++) a[i]=g[stg[fa]+i];for(int i=0;i<=nb;i++) b[i]=mul[st[v]+i];for(int i=0;i<=na-nb;i++) ra[i]=a[na-i];for(int i=0;i<=nb;i++) rb[i]=b[nb-i];for(int i=na-nb+1;i<=nb;i++) rb[i]=0;int len=1,ll=0;while(len<(na-nb+1)*2) len<<=1,ll++;for(int i=0;i<len;i++) f1[i]=0;solve(len,ll);for(int i=0;i<=na-nb;i++) f1[i]=ra[i],f2[i]=irb[i];for(int i=na-nb+1;i<len;i++) f1[i]=f2[i]=0;NTT(f1,len,1),NTT(f2,len,1);for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;NTT(f1,len,-1);for(int i=0;i<=na-nb;i++) c[na-nb-i]=f1[i];len=1,ll=0;while(len<nb<<1) len<<=1,ll++;for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;int w0=ksm(G,(mo-1)/len);for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;for(int i=0;i<nb;i++) f1[i]=b[i],f2[i]=c[i];for(int i=nb;i<len;i++) f1[i]=f2[i]=0;NTT(f1,len,1),NTT(f2,len,1);for(int i=0;i<len;i++) f1[i]=(LL)f1[i]*f2[i]%mo;NTT(f1,len,-1);for(int i=0;i<nb;i++) rr[i]=(a[i]-f1[i]+mo)%mo;for(int i=0;i<=max(na,nb);i++) a[i]=b[i]=c[i]=ra[i]=rb[i]=irb[i]=0;while(nb>1 && !rr[nb-1]) nb--;stg[v]=tot+1;for(int i=0;i<nb;i++) g[++tot]=rr[i],rr[i]=0;eng[v]=tot;}else{stg[v]=tot+1;for(int i=stg[fa];i<=eng[fa];i++) g[++tot]=g[i];eng[v]=tot;}if(l==r){ans[l]=g[stg[v]];return;}int mid=l+r>>1;find(v<<1,l,mid,v);find(v<<1|1,mid+1,r,v);
}
int main()
{int n=read(),m=read();for(int i=0;i<=n;i++) f[i]=read();for(int i=1;i<=m;i++) p[i]=read();make(1,1,m);stg[tot=0]=1;for(int i=0;i<=n;i++) g[++tot]=f[i];eng[0]=tot;find(1,1,m,0);for(int i=1;i<=m;i++) write(ans[i]),putchar('\n');return 0;
}
多项式的求逆、取模和多点求值学习小记相关推荐
- php取模,PHP的求余(取模)运算
这篇文章主要介绍了关于PHP的求余(取模)运算 ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 先来看下个小案例:$n = 8.45; $result = $n*100; echo g ...
- php 求余 负数,PHP的求余(取模)运算
这篇文章主要介绍了关于PHP的求余(取模)运算 ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 先来看下个小案例:$n = 8.45; $result = $n*100; echo g ...
- java求负数取模_负数参与取模运算
学习Python看到数值运算这部分,看到取模运算,原来不仅正数负数都可以取模,浮点数,甚至复数都可以取模: 对于x%y, 如果都是整数,则返回x/y的余数: 如果是浮点数,返回的是x - int(x/ ...
- 多项式快速插值学习小记
今天终于抽空把这个综(du)合(liu)知识点学了,心力交瘁-- 多项式快速插值 给出 nnn 个点 (xi,yi)(x_i,y_i)(xi,yi) ,要求一个次数为 n−1n-1n−1 的多项式 ...
- 多项式的ln、exp、快速幂和开根学习小记
不妨又学习了一下多项式的求ln.exp.快速幂和开根操作. 这些操作比之前的求逆更上了一层台阶,应用同样很广. 多项式求逆等知识在我的博客里有讲:多项式的求逆.取模和多点求值学习小记 多项式ln 给出 ...
- 洛谷P5282 【模板】快速阶乘算法(多项式多点求值+MTT)
题面 传送门 前置芝士 \(MTT\),多项式多点求值 题解 这题法老当初好像讲过--而且他还说这种题目如果模数已经给定可以直接分段打表艹过去 以下是题解 我们设 \[F(x)=\prod_{i=0} ...
- 取模运算性质_求余、取模运算在RTOS中计算优先级的理解
uCOS3中的部分源码: /* 置位优先级表中相应的位 */ void OS_PrioInsert (OS_PRIO prio) { CPU_DATA bit; CPU_DATA bit_nbr; O ...
- python 除数总是提示为0_python负数求余不正确?——取模 VS 取余
前天小王同学正在leetcode兴致勃勃的刷题,用java写了一版后又习惯性的用python写了一版,代码逻辑完全一样,但提交答案后居然提示[解答错误]! 经过反复调试,发现问题出在涉及求余的地方,p ...
- 【转】数学与编程——求余、取模运算及其性质
一.求余运算(Remainder) (参考维基百科: http://zh.wikipedia.org/wiki/余数 http://en.wikipedia.org/wiki/Remainder h ...
最新文章
- python3网络编程
- 给帝国cms7.5后台文章编辑器ckeditor增加一个行距的功能插件
- 初学python还是swift-iOS 将来 Swift 也许会取代 Python
- oracle批量插入并且返回自增主键_oracle 自增主键实现批量更新和增加sql
- FFmpeg音频编解码处理
- java中什么是释放已经持有的锁_java多线程什么时候释放锁
- jQuery validate表单验证demo
- delphi对比易语言_delphi 2007 vs E语言 vs C#运行速度 - Delphi编程
- Python 基礎 - 變量
- 极点五笔的一些快捷键
- 计算机word加边框,Word2010怎样为段落加上边框
- AntV X6源码简析
- mysql数据库默认密码在哪看_怎么查看mysql数据库的登录名和密码
- Windows下的MySQL实例没有mysql.user表#Olivia丶长歌#
- 精确Top-K检索及其加速方法探讨
- lanmp centOS7 一键式 搭建配置
- 理查马文价值导向选股法则
- python量化期权_Python量化期权怎么学?
- 机房收费系统 概要设计
- 劈尖干涉公式_劈尖干涉条纹数的计算
热门文章
- 大数问题(一个特别大的数需要用数组或字符串来表示)
- C++的三种容器适配器
- js-ajax-,JavaScript实现Ajax
- Matlab循环读取txt文件并对其中数据进行计算最后导出为excel
- [云炬创业管理笔记]第一章讨论2
- 科大星云诗社动态20210818
- 返回值类型与函数类型不匹配_C++返回值类型后置(跟踪返回值类型)
- [转载]项目经理必备工具包:项目管理中的22个思维导图
- c++/cli中System::Type::GetType的使用注意事项
- THttprio连接WebService的内存泄漏问题