多项式全家桶
多项式全家桶
前置知识:FFT,NTT,卷积。
多项式乘法(卷积)
最基础的东西。为了给下文打基础这里附上NTT模板及封装卷积。
namespace NTT{
int t[maxn];
void init(int n){
for(int i=1;i<n;i++) t[i]=(t[i>>1]>>1)|(i&1?n>>1:0);
}
void NTT(ll *y,int n,int inv){
for(int i=0;i<n;i++) if(i<t[i]) std::swap(y[t[i]],y[i]);
for(int len=2;len<=n;len<<=1){
ll omg=qpow(inv==1?g:gi,(mod-1)/len);
for(int i=0;i<n;i+=len){
ll cur=1;
for(int j=i;j<i+len/2;j++){
ll e=y[j];ll o=cur*y[j+len/2]%mod;
y[j]=(e+o)%mod;y[j+len/2]=(e-o+mod)%mod;
cur=cur*omg%mod;
}
}
}
if(inv==-1){
ll div=qpow(n,mod-2);
for(int i=0;i<n;i++) y[i]=y[i]*div%mod;
}
}
}
void poly_mul(ll f[],ll g[],int n){//这里先将g复制一份,防止自乘出错
memset(p::ves,0,sizeof p::ves);n=get(n);
for(int i=0;i<n;i++) p::ves[i]=g[i];
NTT::NTT(f,n,1);NTT::NTT(p::ves,n,1);
for(int i=0;i<n;i++) f[i]=p::ves[i]*f[i]%mod;
NTT::NTT(f,n,-1);
}
这里用到了一个 get(x) 函数,是取不小于 x 的最小 \(2^k\)。
int get(int x){
if((x&(x-1))==0) return x;
while((x-1)&x){
x-=-x&x;
}
return x<<1;
}
多项式求逆
设多项式为 \(F\) ,求其逆 \(G\) ,满足 \(F \cdot G\equiv1 \pmod{x^n}\)。用下标表示多项式项数。
若已知
要求 \(G_{2n} \cdot F_{2n}\equiv 1\pmod{x^{2n}}\) 。则有
则可以递归求解。
void poly_inv(ll f[],ll g[],int n){
if(n==1){memset(g,0,sizeof g);g[0]=qpow(f[0],mod-2);return;}
poly_inv(f,g,n>>1);
NTT::init(n<<1);
for(int i=0;i<n;i++) p::x[i]=f[i],p::y[i]=g[i];
for(int i=n;i<n<<1;i++) p::x[i]=p::y[i]=g[i]=0;
NTT::NTT(p::x,n<<1,1),NTT::NTT(p::y,n<<1,1);
for(int i=0;i<n<<1;i++) p::x[i]=p::x[i]*p::y[i]%mod*p::y[i]%mod;
NTT::NTT(p::x,n<<1,-1);
for(int i=0;i<n;i++) adjust(g[i]<<=1),adjust(g[i]=g[i]-p::x[i]);
}
多项式求导/不定积分
没任何难度。
void poly_der(ll f[],ll g[],int n){
for(int i=0;i<n-1;i++) g[i]=(i+1)*f[i+1]%mod;g[n-1]=0;
}
void poly_int(ll f[],ll g[],int n){
for(int i=1;i<=n;i++) g[i]=qpow(i,mod-2)*f[i-1]%mod;g[0]=0;
}
多项式对数函数
对于多项式 \(F(x)\),求 \(G(x)\equiv \ln F(x) \pmod{x^n}\)。
说实话我最早看到这玩意是很吃惊的,这也能整数取模?现在看是我当时比较年轻。
其中要用到求 \(F\) 的逆。
void poly_ln(ll f[],ll g[],int n){
n=get(n);
for(int i=0;i<n<<1;i++) p::A[i]=p::B[i]=g[i]=0;
poly_der(f,p::A,n);poly_inv(f,p::B,n);
poly_mul(p::A,p::B,n<<1);
poly_int(p::A,g,n);
}
多项式牛顿迭代
假如我们已知函数 \(f(x)\),我们对其在 \(x_0\) 处泰勒展开可得到:
泰勒公式本质是个构造函数,当构造的函数在 \(x_0\) 的高次导数都等于原函数的高次导数,那么原函数可以用无限次的多项式函数近似表达。
在多项式中,
我们甚至可以在一个多项式处对复合函数展开:
如果有函数 \(g(f(x))\),其中 \(f(x)\) 是多项式,如果我们对多项式 \(f_0(x)\) 展开,有
这里不能将 \(x\) 理解为变量,因为 \(x\) 在多项式中没有什么实际意义,这里自变量是 \(f(x)\),\(f_0(x)\) 是常量。所以将泰勒展开用多项式替换,得到上式。
在牛顿迭代中,我们取泰勒展开的前两项,即将多项式近似成一个切线,求这个切线的根,然后再用这个根展开,反复迭代,可以得到一个近似的根。
在多项式中,略有点区别:
假如我们有 \(G(F_0)\equiv 0 \pmod{x^n}\) ,我们要求 \(G(F)\equiv0 \pmod{x^{2n}}\)。
我们对 \(G\) 在 \(F_0\) 处泰勒展开,
注意到 \(F-F_0 \equiv 0 \pmod{x^n}\),因此 \((F-F_0)^2 \equiv 0 \pmod{x^{2n}}\)。更高次同理,于是得到
由于 \(G(F)\equiv0 \pmod{x^{2n}}\) ,所以得到
这是多项式牛顿迭代的基本形式。
多项式指数函数
有多项式 \(A(x)\) ,求 \(B(x) \equiv e^{A(x)} \pmod{x^n}\) 。
有
这是关于 \(B(x)\) 的方程。用牛顿迭代求解。令 \(G(B(x))=\ln B(x)-A(x)\)。
\(G'(B(x))=\frac{1}{B(x)}\)。若已知 \(B_0(x)\) 在递归底层满足式子,则有
可得
void poly_exp(ll f[],ll g[],int n){
if(n==1) return (void)(g[0]=1);
poly_exp(f,g,n>>1);poly_ln(g,p::z,n);
NTT::init(n<<1);
adjust(p::z[0]=f[0]-p::z[0]+1);
for(int i=1;i<n;i++) adjust(p::z[i]=f[i]-p::z[i]);
for(int i=n;i<(n<<1);i++) p::z[i]=g[i]=0;
NTT::NTT(p::z,n<<1,1),NTT::NTT(g,n<<1,1);
for(int i=0;i<n<<1;i++) g[i]=g[i]*p::z[i]%mod;
NTT::NTT(g,n<<1,-1);for(int i=n;i<n<<1;i++) g[i]=0;
}
多项式快速幂
给定多项式 \(A(x)\),求 \(B(x)\equiv A^k(x) \pmod{x^n}\)。
有
直接套板子。
void poly_pow(ll f[],ll g[],int n,ll k){
for(int i=0;i<n;i++) p::C[i]=0;
poly_ln(f,p::C,n);
for(int i=0;i<n;i++) p::C[i]=p::C[i]*k%mod;
poly_exp(p::C,g,n);
}
多项式平方根
给定多项式 \(A(x)\),求 \(B^2(x)\equiv A(x) \pmod{x^n}\)
也就是我们要求方程 \(G(B(x))=B^2(x)-A(x)\equiv 0 \pmod{x^n}\) 的解。这里只求常数项较小的根。
根据牛顿迭代,有
因为 \(G'(B(x))=2B(x)\) ,可得
\(B(x)\equiv B_0(x) -\frac{B_0^2(x)-A(x)}{2B_0(x)}\equiv \frac{B_0^2(x)+A(x)}{2B_0(x)} \pmod{x^{2n}}\)。
const ll bdiv=qpow(2,mod-2);
void poly_sqrt(ll f[],ll g[],int n){
if(n==1) return (void)(g[0]=1);
poly_sqrt(f,g,n>>1);NTT::init(n<<1);
poly_inv(g,p::A,n);
for(int i=0;i<n;i++) p::A[i]=p::A[i]*bdiv%mod;
for(int i=0;i<n;i++) p::B[i]=f[i];
for(int i=n;i<n<<1;i++) p::A[i]=p::B[i]=g[i]=0;
NTT::NTT(p::A,n<<1,1),NTT::NTT(p::B,n<<1,1),NTT::NTT(g,n<<1,1);
for(int i=0;i<n<<1;i++) g[i]=(g[i]*g[i]+p::B[i])%mod*p::A[i];
NTT::NTT(g,n<<1,-1);for(int i=n;i<n<<1;i++) g[i]=0;
}

浙公网安备 33010602011771号