芝士:多项式的瓜皮操作
本博客较长,建议分批次食用
前置芝士
lim
\(lim_{x\rightarrow y}\)表示x趋近于y,但x小于y,y是常数
求导&导数
当自变量的增量趋近于0时,因变量的增量和自变量之商的极限。
如果我们已知函数\(f(x)\),那么我们对其求导之后的函数称为\(f'(x)\)
也就是求极限\(lim_{\delta\rightarrow 0}f'(x)=\frac{f(x+\delta)-f(x)}{\delta}\)
导数就是\(lim_{\delta\rightarrow 0}f'(x)=\frac{f(x+\delta)-f(x)}{\delta}\)
有性质
\((f(g(x)))'=f'(g(x))*g'(x)\)
对数
若\(a^n=b\),那么\(log_a^b=n\),我们称\(log\)为对数函数
自然常数
定义\(lim_{x\rightarrow+\infty}e=(1+\frac{1}{x})^x\)
自然对数
定义\(ln\space N=log_{e}^{N}\)
有性质
\(ln'(x)=\frac{1}{x}\)
泰勒展开
公式:
\(f(x)=\sum_{i=0}^{n}(\frac{f^{'(i个)}(x_0)}{i!}*(x-x_0)^i)+R_n(x)\)
\(x_0\)是个随机数
\(f^{'(i个)}\)表示\(f^{'(i-1个)}\)的导数
\(R_n(x)\)表示偏差
巨佬泰勒告诉我们当n足够大时,偏差就越小
也就是
\(f(x)=\sum_{i=0}^{\infty}\frac{f^{'(i个)}(x_0)}{i!}*(x-x_0)^i)\)
牛顿迭代
我们设\(r\)是\(f(x)=0\)的根
公式为
\(x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}\)
\(x_0\)是一个随机值
巨佬牛顿告诉我们,迭代次数越多,
\(x_n\)就越接近于\(r\)
不定积分
根据笔者粗略的理解
设\(A\)为一个函数
就是\(A=\int A^{-1}\)
定积分
想象\(f\)这一个函数在坐标系上的样子
别介意图片马赛克一样的画质
如果我们想求\((x_1,y_1),(x_n,y_n)\)的曲线的面积
我们实际上可以将其分成许多个小的矩形,
不难想到,当\(\Delta x\)足够小时,标红区域的面积就会越来越接近正确答案
我们将其表示为
\(\int_{a}^{b}(f(x)*dx)\)
其中\(dx\)就为\(\Delta x\)
当然如果还不能理解的话,请看下面这一个公式
\(\int_{a}^{b}(f(x)*dx)=lim_{x\rightarrow+\infty}\sum_{i=1}^{n}(f(a+\frac{i}{n}(b-a))*\frac{b-a}{n})\)
你可以将其理解为将区间\([a,b]n\)等分
正题
多项式の乘
傅里叶说:这一项我包揽了
于是就有了FFT和改进的NTT
在此,笔者不要脸的链了个自己的博客
多项式の逆元
推导
给定\(A(x)\),求\(A^{-1}(x)\)满足
\(A(x)A^{-1}(x)\equiv1\pmod{x^n}\)
Tips:我们称\(A^{-1}\)为\(A\)在\(\pmod{x^n}\)意义下的逆元
注意此时的\(\pmod{x^n}\)为舍去次数\(\geq n\)的项数,只保留\(0\)到\(n-1\)的项
还有一点很明显的是
当\(n=1\)时,答案可以直接快速幂求出
虽然这是一个很明显的性质,但是它极其有用
我们假设已经求出了\(\pmod {x^n}\)意义下\(A\)的逆元\(B_0\),显然有
\(A(x)*B_0(x)\equiv 1\pmod{x^n}\)
我们想要的函数\(B\),满足
\(A(x)*B(x)\equiv 1\pmod {x^{2n}}\)
两式相减可以得到
\(A(x)*(B(x)-B_0(x))\equiv0 \pmod{x^n}\)
为什么是\(\pmod {x^n}\)呢?
我们这样考虑
我们设\(f(x)=A(x)*B(x)\)
那么\(f(x)\equiv1\pmod {x^n}\)
注意此时的\(f\)是对任意\(x\)都成立的
所以\(f\)的表达式一定是只有常数项的
我们继续考虑处理式子,两边同时除以\(A\)
则得到
\(B(x)-B_0(x)\equiv 0 \pmod{x^n}\)
你可以通过这个式子推出\(B和B_0\)前n-1项的一些性质
也就是前n-1项全部相等
因为我们要从\(x^n\rightarrow x^{2n}\),所以我们考虑平方一下
\((B(x)-B_0(x))^2\equiv0\pmod{x^{2n}}\)
为什么这里是\(2n\),就跟\(B和B_0\)前n-1项相等有关
将其展开
\(B(x)^2-2B(x)B_0(x)+B^2_0(x)\equiv 0\pmod{x^{2n}}\)
在乘一个\(A\),可以得到
\(B(x)-2*B_0(x)+A(x)*B_0^2(x)\equiv0\pmod{x^{2n}}\)
即
\(B(x)\equiv2*B_0(x)-A(x)*B_0^2(x)\pmod{x^{2n}}\)
\(B(x)\equiv B_0(x)*(2-A(x)*B_0(x))\pmod {x^{2n}}\)
在这里,因为\(B\)的次数超过\(x^{2n}\)就没有意义了
所以可以直接当作等于
代码
poly inv (const poly &f)//逆元
{
int a[M]={};
int b[M]={};
poly g;
g.push_back(inv(f[0]));
for(int len=2;(len>>1)<f.size();len<<=1)
{
int lim=init(len<<1);
int inv_len=inv(lim);
memset(&a[len],0,len*SIZE);
memset(&b[len],0,len*SIZE);
for(int i=0;i<len;i++)
a[i]=i<f.size()?f[i]:0;
for(int i=0;i<len;i++)
b[i]=i<g.size()?g[i]:0;
ntt(a,lim);
ntt(b,lim);
for(int i=0;i<lim;i++)
a[i]=(1ll*a[i]*b[i]%mod)*b[i]%mod;
reverse(a+1,a+lim);
ntt(a,lim);
g.resize(len);
for(int i=0;i<g.size();i++)
g[i]=sub(add(g[i],g[i]),1ll*a[i]*inv_len%mod);
g.resize(f.size());
}
g.resize(f.size());
return g;
}
多项式の除法&多项式の取模
推导
给定\(n-1\)次多项式\(A(x)\)和\(m-1\)次多项式\(B(x)\),求\(D(x)和R(x)\),满足
\(A(x)=B(x)*D(x)+R(x)\)
\(R\)的最高次项\(< m-1\)
同时满足
\(A(x)\equiv R(x)\pmod{B(x)}\)
我们定义反转操作
\(A^R(x)=x^{n-1}*A(\frac{1}{x})=\sum_{i=0}^{n-1}(a_{n-i-1}*x_i)\)
如果我们将\(\frac{1}{x}\)带入式子
\(A(\frac{1}{x})=D(\frac{1}{x})*B(\frac{1}{x})+R(\frac{1}{x})\)
再同乘一个\(x^{n-1}\),(因为我们要针对\(\frac{1}{x}\) )
可得到
\(x^{n-1}A(\frac{1}{x})=x^{n-m}D(\frac{1}{x})x^{m+1}B(\frac{1}{x})+x^{n-m+1}x^{m-2}R(\frac{1}{x})\)
这个式子像什么?
不就是反转操作?
\(A^R(x)=D^R(x)B^R(x)+x^{n-m+1}R^R(x)\)
反转操作并不会改变\(D\)的最高次项,所以在\(\pmod {x^{n-m+1}}\)的意义下,我们就能消除\(R\)的影响
\(A^R(x)\equiv D^R(x)B^R(x)\pmod{x^{n-m+1}}\)
\(\frac{A^R(x)}{B^R(x)}\equiv D^R(x)\pmod{x^{n-m+1}}\)
由于\(D^R\)的最高次项就已经小于\(x^{n-m+1}\),所以可以写等于
注意是在\(\pmod{x^{x-m+1}}\)的意义下
代码
poly resize(poly f,int n)
{
f.resize(n);
return f;
}
void div (poly f,poly q,poly &g,poly &r)
{
int lenq=q.size();
reverse(f.begin(),f.end());
reverse(q.begin(),q.end());
poly _q=q;
q=resize(q,f.size()-q.size()+1);
g=resize(f*inv(q),f.size()-lenq+1);
reverse(g.begin(),g.end());
reverse(f.begin(),f.end());
q=_q;
reverse(q.begin(),q.end());
r=resize(f-q*g,lenq-1);
}
多项式の牛顿迭代法
推导
这是一种思想,并没有任何代码
已知\(g(f(x))=0\),同时\(g(x)\)已知,求\(f(x)\)
假设我们已经知道\(f(x)\)的前n项,\(f_0(x)\)
\(\begin{cases}f(x)\equiv f_0(x)&\pmod{x^n}\\g(f_0(x))\equiv0&\pmod{x^n}\end{cases}\)
之后我们大力泰勒展开
\(g(f(x))=\sum_{i=0}^{\infty}(\frac{g'^{i个}(f_0(x))}{i!}*(f(x)-f_0(x))^i)\)
之后我们根据\(f_0(x)\)的定义
\(g(f(x))\equiv g(f_0(x))+g'(f_0(x))(f(x)-f_0(x))\equiv0\pmod{x^{2n}}\)
解释一下为什么是\(x^{2n}\),我们从二次项看,\(f(x)-f_0(x)\)的前n项为0,所以平方之后前\(2n-2\)项就为0
注意这里是项,不是次
我们再考虑\(2n-1\)项,想想他的构成
\(\sum_{i=0}^{2n-1}a_ia_{2n-1-i}\),注意前n项为0
所以第\(2n-1\)项为0
之后
\(f(x)\equiv f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))}\pmod{2^n}\)
多项式の开根
推导
给定多项式\(A(x)\),求\(B(x)\),满足
\(B^2(x)-A(x)\equiv 0\pmod{x^n}\)
设\(B_0(x)\equiv B(x)\pmod {x^n}\)
大力迭代
\(B(x)\equiv B_0(x)-\frac{B_0^2(x)-A(x)}{2B_0(x)}\pmod {x^{2n}}\)
\(B(x)\equiv\frac{2*B_0^2(x)-B_0^2(x)+A(x)}{2B_0{x}}\pmod{2^n}\)
\(B(x)\equiv\frac{B_0^2(x)+A(x)}{2*B_0(x)}\pmod{2^n}\)
那么问题来了,当\(n=1\)时,怎么办呢?
cipolla不就完事了?
代码
namespace cipolla_namespace//二次剩余
{
#define x first
#define y second
int t;
int sqrt_w;
pair<int,int> p;
pair<int,int> operator * (const pair<int,int> &a,const pair<int,int> &b)
{
return make_pair((1ll*a.x*b.x+1ll*a.y*b.y%mod*sqrt_w)%mod,(1ll*a.x*b.y+1ll*a.y*b.x)%mod);
}
int cipolla(int x)
{
do
{
t=rand()%mod;
sqrt_w=sub(1ll*t*t%mod,x);
}
while(qkpow(sqrt_w,(mod-1)>>1)!=mod-1);
pair<int,int> s=make_pair(1,0);
pair<int,int> a=make_pair(t,1);
for(int b=(mod+1)>>1;b;b>>=1,a=a*a)
if(b&1)
s=s*a;
return min(s.x,mod-s.x);
}
#undef x
#undef y
}using cipolla_namespace::cipolla;
poly sqrt(const poly &f)//开根
{
poly g;
g.push_back(cipolla(f[0]));
for(int len=2;(
g=resize(resize(resize(g*g,len)+f,len)*inv(resize(2*g,len)),len);
g.resize(f.size());
return g;
}
多项式のln
推导
对于一个多项式\(A(x)\),求\(ln(A(x))\pmod{x^n}\)
大力推导
\(ln(A(x))=\int (ln(A(x)))'=\int (ln'(A(x))*A'(x))=\int\frac{A'(x)}{A(x)}\pmod{x^n}\)
代码
poly deri(const poly &f)//求导
{
poly g;
for(int i=0;i<f.size()-1;i++)
g.push_back(1ll*(i+1)*f[i+1]%mod);
g.push_back(0);
return g;
}
poly inte(poly f)//积分
{
poly g;
g.push_back(0);
for(int i<f.size()-1;i++)
g.push_back(1ll*inv(i+1)*f[i]%mod);
return g;
}
poly ln(const poly &f)
{
return inte(resize(deri(f)*inv(f),f.size()));
}
多项式のexp
推导
对于一个多项式\(A(x)\),求
\(e^{A(x)}\pmod {x^n}\)
可以假设\(B(x)\equiv e^{A(x)}\pmod{x^n}\)
\(ln(B(x))\equiv A(x)\pmod {x^n}\)
\(ln(B(x))-A(x)\equiv0\pmod{x^n}\)
再设\(B_0(x)\equiv B(x)\pmod{x^n}\)
大力牛顿迭代
\(B(x)\equiv B_0(x)-\frac{ln(B_0(x))-A(x)}{\frac{1}{B_0(x)}}\pmod{x^{2n}}\)
再将下面的倒上来
\(B(x)\equiv B_0(x)(1-ln(B_0(x))+A(x))\pmod{x^{2n}}\)
代码
poly exp(const poly &f)
{
poly g;
g.push_back(1);
for(int len=2;(len>>1)<f.size();len<<=1)
g=resize(g*(1-ln(resize(g,len))+resize(f,len)),len);
g.resize(f.size());
return g;
}
多项式のk次幂
推导
给定多项式\(F(x)\),和正整数k,求\(F^k(x)\),的前n项系数
大力快速幂
代码
poly qkpow(poly a,int b)
{
int n=a.size();
poly s;
s.push_back(1);
while(b)
{
if(b&1)
s=resize(s*a,n);
a=resize(a*a,n);
b>>=1;
}
return s;
}
封装
笔者在此不要脸的再次链了个链接
虽然原题没有除法,但是笔者还是将除法封装了进去

浙公网安备 33010602011771号