生成函数多项式操作合集

一.生成函数与多项式乘法.

众所周知,生成函数是OI中的计数利器,而很多生成函数的题目需要快速计算式子中的多项式运算,所以本篇主要是介绍多项式运算的.

值得注意的是,这里很多奇怪的多项式运算本来是不存在模 x n x^n xn意义下的定义的,但是由于生成函数本质是形式幂级数,所以直接取泰勒展开后的结果就是了.

多项式乘法作为最基础的多项式运算,这里不再介绍,具体参见快速傅里叶变换FFT与快速数论变换NTT入门.


二.多项式求逆.

多项式的逆:对于一个多项式 F ( x ) F(x) F(x),其逆 G ( x ) G(x) G(x)定义为:
G ( x ) F ( x ) = 1 G(x)F(x)=1 G(x)F(x)=1

但很多情况下 G ( x ) G(x) G(x)是一个无穷级数的形式,所以一般我们只需要取其最后 n n n项(次数低于 n n n次),记为:
G ( x ) F ( x ) ≡ 1    ( m o d    x n ) G(x)F(x)\equiv 1\,\,(mod\,\,x^{n}) G(x)F(x)1(modxn)

当然可以暴力求解,但是有一种倍增的思路:我们假设已经求出了模 x ⌈ n 2 ⌉ x^{\left\lceil\frac{n}{2}\right\rceil} x2n意义下 F ( x ) F(x) F(x)的逆 H ( x ) H(x) H(x),推出 G ( x ) G(x) G(x) H ( x ) H(x) H(x)之间的关系.

现在有两个条件:
G ( x ) F ( x ) ≡ 1    ( m o d    x ⌈ n 2 ⌉ ) H ( x ) F ( x ) ≡ 1    ( m o d    x ⌈ n 2 ⌉ ) G(x)F(x)\equiv 1 \,\,(mod\,\,x^{\left\lceil\frac{n}{2}\right\rceil})\\ H(x)F(x)\equiv 1\,\,(mod\,\,x^{\left\lceil\frac{n}{2}\right\rceil}) G(x)F(x)1(modx2n)H(x)F(x)1(modx2n)

将两式相减可得:
( G ( x ) − H ( x ) ) F ( x ) ≡ 0    ( m o d    x ⌈ n 2 ⌉ ) G ( x ) − H ( x ) ≡ 0    ( m o d    x ⌈ n 2 ⌉ ) (G(x)-H(x))F(x)\equiv 0\,\,(mod\,\,x^{\left\lceil\frac{n}{2}\right\rceil})\\ G(x)-H(x)\equiv 0\,\,(mod\,\,x^{\left\lceil\frac{n}{2}\right\rceil}) (G(x)H(x))F(x)0(modx2n)G(x)H(x)0(modx2n)

再平方一下可以得到:
( G ( x ) − H ( x ) ) 2 ≡ 0    ( m o d    x n ) G 2 ( x ) + H 2 ( x ) − 2 G ( x ) H ( x ) ≡ 0    ( m o d    x n ) (G(x)-H(x))^{2}\equiv 0\,\,(mod\,\,x^{n})\\ G^{2}(x)+H^{2}(x)-2G(x)H(x)\equiv 0\,\,(mod\,\,x^{n}) (G(x)H(x))20(modxn)G2(x)+H2(x)2G(x)H(x)0(modxn)

两边同时乘上 F ( x ) F(x) F(x)得到:
F ( x ) G 2 ( x ) + F ( x ) H 2 ( x ) − 2 F ( x ) G ( x ) H ( x ) ≡ 0    ( m o d    x n ) G ( x ) + F ( x ) H 2 ( x ) − 2 H ( x ) ≡ 0    ( m o d    x n ) G ( x ) ≡ 2 H ( x ) − F ( x ) H 2 ( x )    ( m o d    x n ) G ( x ) ≡ H ( x ) ( 2 − F ( x ) H ( x ) )    ( m o d    x n ) F(x)G^{2}(x)+F(x)H^{2}(x)-2F(x)G(x)H(x)\equiv 0\,\,(mod\,\,x^{n})\\ G(x)+F(x)H^{2}(x)-2H(x)\equiv 0\,\,(mod\,\,x^{n})\\ G(x)\equiv 2H(x)-F(x)H^{2}(x)\,\,(mod\,\,x^{n})\\ G(x)\equiv H(x)(2-F(x)H(x))\,\,(mod\,\,x^{n}) F(x)G2(x)+F(x)H2(x)2F(x)G(x)H(x)0(modxn)G(x)+F(x)H2(x)2H(x)0(modxn)G(x)2H(x)F(x)H2(x)(modxn)G(x)H(x)(2F(x)H(x))(modxn)

然后就可以快乐倍增了.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

int ta[N+9];

void Poly_inv(int *a,int n,int *res){
  if (!n) {res[0]=Get_inv(a[0]);return;}
  Poly_inv(a,n>>1,res);
  for (int i=0;i<=n;++i) ta[i]=a[i];
  Get_len(n<<1);
  NTT(res,len,0);
  NTT(ta,len,0);
  for (int i=0;i<len;++i) smul(res[i],sub(2,mul(res[i],ta[i]))),ta[i]=0;
  NTT(res,len,1);
  for (int i=n+1;i<=n<<1;++i) res[i]=0;
}



三.多项式对数.

多项式对数主要是指多项式 ln ⁡ \ln ln,即给定多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x)满足:
e G ( x ) ≡ F ( x )    ( m o d    x n ) e^{G(x)}\equiv F(x)\,\,(mod\,\,x^{n}) eG(x)F(x)(modxn)

对数函数在求导后就变成除法了,所以尝试先求导再积分:
ln ⁡ F ( x ) = ∫ ( ln ⁡ F ( x ) ) ′ d x = ∫ ln ⁡ ′ F ( x ) F ′ ( x ) d x = ∫ F ′ ( x ) F ( x ) d x \ln F(x)=\int(\ln F(x))'\mathrm{d}x=\int\ln'F(x)F'(x)\mathrm{d}x=\int\frac{F'(x)}{F(x)}\mathrm{d}x lnF(x)=(lnF(x))dx=lnF(x)F(x)dx=F(x)F(x)dx

现在的式子就可以直接做了.

不过有一个问题是,这样子多项式 ln ⁡ \ln ln必须得满足 F ( x ) F(x) F(x)的常数项为 1 1 1,最终求出的 G ( x ) G(x) G(x)的常数项为 0 0 0.

至于原因,主要是这样按照泰勒展开定义的多项式 ln ⁡ \ln ln必须满足这样的条件.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

void Poly_ln(int *a,int n,int *res){
  if (!n) return;
  Poly_inv(a,n-1,res);
  for (int i=1;i<=n;++i) ta[i-1]=mul(i,a[i]);
  Get_len(n-1<<1);
  NTT(res,len,0);
  NTT(ta,len,0);
  for (int i=0;i<len;++i) smul(res[i],ta[i]),ta[i]=0;
  NTT(res,len,1);
  for (int i=n;i>=1;--i) res[i]=mul(inv[i],res[i-1]);
  res[0]=0;
  for (int i=n+1;i<n<<1;++i) res[i]=0;
}



四.牛顿迭代.

牛顿迭代是一种估算方程解的方法,不过这里我们直接应用到多项式上了.

对于给定的规则函数 T ( x ) T(x) T(x),我们有方程:
T ( F ( x ) ) ≡ 0    ( m o d    x n ) T(F(x))\equiv 0\,\,(mod\,\,x^{n}) T(F(x))0(modxn)

现在我们要求 F ( x ) F(x) F(x).

考虑倍增求解,如果我们已经求出了在模 x ⌈ n 2 ⌉ x^{\left\lceil\frac{n}{2}\right\rceil} x2n意义下的解 G ( x ) G(x) G(x),即:
T ( G ( x ) ) ≡ 0    ( m o d    x ⌈ n 2 ⌉ ) T(G(x))\equiv 0\,\,(mod\,\,x^{\left\lceil\frac{n}{2}\right\rceil}) T(G(x))0(modx2n)

那么我们将 T ( F ( x ) ) T(F(x)) T(F(x)) G ( x ) G(x) G(x)处泰勒展开:
T ( F ( x ) ) = ∑ i = 0 + ∞ T ( i ) ( G ( x ) ) i ! ( F ( x ) − G ( x ) ) i = T ( G ( x ) ) + T ′ ( G ( x ) ) ( F ( x ) − G ( x ) ) + 1 2 T ′ ′ ( G ( x ) ) ( F ( x ) − G ( x ) ) 2 + ⋯ T(F(x))=\sum_{i=0}^{+\infty}\frac{T^{(i)}(G(x))}{i!}(F(x)-G(x))^{i}\\ =T(G(x))+T'(G(x))(F(x)-G(x))+\frac{1}{2}T''(G(x))(F(x)-G(x))^{2}+\cdots T(F(x))=i=0+i!T(i)(G(x))(F(x)G(x))i=T(G(x))+T(G(x))(F(x)G(x))+21T(G(x))(F(x)G(x))2+

由于我们求的是模 x n x^{n} xn意义下的结果,所以对于 ( F ( x ) − G ( x ) ) 2 (F(x)-G(x))^{2} (F(x)G(x))2有:
( F ( x ) − G ( x ) ) 2 ≡ 0    ( m o d    n ) (F(x)-G(x))^{2}\equiv 0\,\,(mod\,\,n) (F(x)G(x))20(modn)

也就是说:
T ( F ( x ) ) ≡ 0    ( m o d    x n ) T ( G ( x ) ) + T ′ ( G ( x ) ) ( F ( x ) − G ( x ) ) ≡ 0    ( m o d    x n ) F ( x ) ≡ G ( x ) − T ( G ( x ) ) T ′ ( G ( x ) )    ( m o d    x n ) T(F(x))\equiv 0\,\,(mod\,\,x^{n})\\ T(G(x))+T'(G(x))(F(x)-G(x))\equiv 0\,\,(mod\,\,x^{n})\\ F(x)\equiv G(x)-\frac{T(G(x))}{T'(G(x))}\,\,(mod\,\,x^{n}) T(F(x))0(modxn)T(G(x))+T(G(x))(F(x)G(x))0(modxn)F(x)G(x)T(G(x))T(G(x))(modxn)

推导出来的结果在做多项式操作的时候非常有效.


五.多项式指数.

多项式指数主要是指多项式 exp ⁡ \exp exp,即给定多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x)满足:
G ( x ) ≡ e F ( x )    ( m o d    x n ) G(x)\equiv e^{F(x)}\,\,(mod\,\,x^{n}) G(x)eF(x)(modxn)

我们先对两边求 ln ⁡ \ln ln,得到:
ln ⁡ G ( x ) ≡ F ( x )    ( m o d    x n ) ln ⁡ G ( x ) − F ( x ) ≡ 0    ( m o d    x n ) \ln G(x)\equiv F(x)\,\,(mod\,\,x^{n})\\ \ln G(x)-F(x)\equiv 0\,\,(mod\,\,x^{n}) lnG(x)F(x)(modxn)lnG(x)F(x)0(modxn)

设在模 x ⌈ n 2 ⌉ x^{\left\lceil\frac{n}{2}\right\rceil} x2n意义下的答案为 H ( x ) H(x) H(x),那么根据牛顿迭代公式有:
G ( x ) ≡ H ( x ) − ln ⁡ H ( x ) − F ( x ) 1 H ( x )    ( m o d    x n ) G ( x ) ≡ H ( x ) ( 1 − ln ⁡ H ( x ) + F ( x ) )    ( m o d    x n ) G(x)\equiv H(x)-\frac{\ln H(x)-F(x)}{\frac{1}{H(x)}}\,\,(mod\,\,x^{n})\\ G(x)\equiv H(x)\left(1-\ln H(x)+F(x)\right)\,\,(mod\,\,x^{n}) G(x)H(x)H(x)1lnH(x)F(x)(modxn)G(x)H(x)(1lnH(x)+F(x))(modxn)

然后就可以快乐倍增了.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

ln ⁡ \ln ln相对的, exp ⁡ \exp exp必须满足 F ( x ) F(x) F(x)常数项为 0 0 0 G ( x ) G(x) G(x)常数项为 1 1 1的条件.

代码如下:

int tb[N+9];

void Poly_exp(int *a,int n,int *res){
  if (!n) {res[0]=1;return;}
  Poly_exp(a,n>>1,res);
  Poly_ln(res,n,tb);
  tb[0]=sub(add(a[0],1),tb[0]);
  for (int i=1;i<=n;++i) tb[i]=sub(a[i],tb[i]);
  Get_len(n<<1);
  NTT(res,len,0);
  NTT(tb,len,0);
  for (int i=0;i<len;++i) smul(res[i],tb[i]),tb[i]=0;
  NTT(res,len,1);
  for (int i=n+1;i<=n<<1;++i) res[i]=0;
}



六.多项式快速幂.

给定多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x)满足:
G ( x ) ≡ F k ( x )    ( m o d    x n ) G(x)\equiv F^{k}(x)\,\,(mod\,\,x^{n}) G(x)Fk(x)(modxn)

当然可以直接拿快速幂写,但是这样时间复杂度为 O ( n log ⁡ n log ⁡ k ) O(n\log n\log k) O(nlognlogk),还有更加优秀的做法.

我们考虑对其 ln ⁡ \ln ln exp ⁡ \exp exp,即:
G ( x ) ≡ e k ln ⁡ F ( x )    ( m o d    x n ) G(x)\equiv e^{k\ln F(x)}\,\,(mod\,\,x^{n}) G(x)eklnF(x)(modxn)

问题是这个做法只能用在常数项为 1 1 1的情况.

对于常数项不为 1 1 1的情况,我们取其最低的不为 0 0 0的项,设为 a m x m a_mx^{m} amxm,那么直接将这个单项式作为因式提出来后再做快速幂,最后将 ( a m x m ) k (a_mx^{m})^{k} (amxm)k乘回去就好了.

时间复杂度降到了 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

int tc[N+9];

void Poly_power(int *a,int n,int k,int *res){
  if (!k) {res[0]=1;return;}
  int m=0;
  for (;!a[m]&&m<=n;++m);
  if (m>n/k) return;
  int mk=m*k,t0=Power(a[m],k),t1=Get_inv(a[m]);
  for (int i=0;i<=n-mk;++i) tb[i]=mul(a[i+m],t1);
  Poly_ln(tb,n-mk,tc);
  for (int i=0;i<=n-mk;++i) smul(tc[i],k),tb[i]=0;
  Poly_exp(tc,n-mk,res);
  for (int i=n-mk;i>=0;--i) res[i+mk]=mul(res[i],t0),tc[i]=0;
  for (int i=0;i<mk;++i) res[i]=0;
}



七.多项式开根.

给定多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x)满足:
G 2 ( x ) ≡ F ( x )    ( m o d    x n ) G^{2}(x)\equiv F(x)\,\,(mod\,\,x^{n}) G2(x)F(x)(modxn)

当然可以直接 ln ⁡ \ln ln exp ⁡ \exp exp,但是还有一种常数更加优秀的牛顿迭代做法.

直接进行移项,得到:
G 2 ( x ) − F ( x ) ≡ 0    ( m o d    x n ) G^{2}(x)-F(x)\equiv 0\,\,(mod\,\,x^{n}) G2(x)F(x)0(modxn)

设在模 x ⌈ n 2 ⌉ x^{\left\lceil \frac{n}{2}\right\rceil} x2n意义下的答案为 H ( x ) H(x) H(x),那么根据牛顿迭代公式有:
G ( x ) ≡ H ( x ) − H 2 ( x ) − F ( x ) 2 H ( x )    ( m o d    x n ) G ( x ) ≡ 1 2 ( H ( x ) + F ( x ) H ( x ) )    ( m o d    x n ) G(x)\equiv H(x)-\frac{H^{2}(x)-F(x)}{2H(x)}\,\,(mod\,\,x^{n})\\ G(x)\equiv \frac{1}{2}\left(H(x)+\frac{F(x)}{H(x)}\right)\,\,(mod\,\,x^{n}) G(x)H(x)2H(x)H2(x)F(x)(modxn)G(x)21(H(x)+H(x)F(x))(modxn)

至于常数项写个二次剩余就行了.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

void Poly_sqrt(int *a,int n,int *res){
  if (!n) {int t=Cipolla(a[0]);res[0]=min(t,mod-t);return;}
  Poly_sqrt(a,n>>1,res);
  Poly_inv(res,n,tb);
  for (int i=0;i<=n;++i) ta[i]=a[i];
  Get_len(n<<1);
  NTT(ta,len,0);
  NTT(tb,len,0);
  for (int i=0;i<len;++i) smul(ta[i],tb[i]),tb[i]=0;
  NTT(ta,len,1);
  for (int i=0;i<=n;++i) res[i]=mul(inv2,add(res[i],ta[i])),ta[i]=0;
  for (int i=n+1;i<=n<<1;++i) ta[i]=0;
}



八.多项式三角函数.

给定多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x) H ( x ) H(x) H(x)满足:
G ( x ) ≡ sin ⁡ F ( x )    ( m o d    x n ) H ( x ) ≡ cos ⁡ F ( x )    ( m o d    x n ) G(x)\equiv \sin F(x)\,\,(mod\,\,x^{n})\\ H(x)\equiv \cos F(x)\,\,(mod\,\,x^{n}) G(x)sinF(x)(modxn)H(x)cosF(x)(modxn)

生成函数本质上是形式幂级数,所以将 e x , sin ⁡ ( x ) , cos ⁡ ( x ) e^{x},\sin(x),\cos(x) ex,sin(x),cos(x)三者泰勒展开:
e x = ∑ i = 0 + ∞ x i i ! = 1 + x + x 2 2 ! + x 3 3 ! + ⋯ sin ⁡ ( x ) = ∑ i = 0 + ∞ ( − 1 ) i ( 2 i + 1 ) ! x 2 i + 1 = x − x 3 3 ! + x 5 5 ! − x 7 7 ! + ⋯ cos ⁡ ( x ) = ∑ i = 0 + ∞ ( − 1 ) i ( 2 i ) ! x 2 i = 1 − x 2 2 ! + x 4 4 ! − x 6 6 ! + ⋯ e^{x}=\sum_{i=0}^{+\infty}\frac{x^{i}}{i!}=1+x+\frac{x^{2}}{2!}+\frac{x^{3}}{3!}+\cdots\\ \sin(x)=\sum_{i=0}^{+\infty}\frac{(-1)^{i}}{(2i+1)!}x^{2i+1}=x-\frac{x^{3}}{3!}+\frac{x^{5}}{5!}-\frac{x^{7}}{7!}+\cdots\\ \cos(x)=\sum_{i=0}^{+\infty}\frac{(-1)^{i}}{(2i)!}x^{2i}=1-\frac{x^{2}}{2!}+\frac{x^{4}}{4!}-\frac{x^{6}}{6!}+\cdots ex=i=0+i!xi=1+x+2!x2+3!x3+sin(x)=i=0+(2i+1)!(1)ix2i+1=x3!x3+5!x57!x7+cos(x)=i=0+(2i)!(1)ix2i=12!x2+4!x46!x6+

用这些东西可以推导出用三角函数表示 exp ⁡ \exp exp的欧拉公式:
e ϕ i = cos ⁡ ϕ + i sin ⁡ ϕ e^{\phi i}=\cos \phi+i\sin \phi eϕi=cosϕ+isinϕ

那么同样可以用这些东西推导出用 exp ⁡ \exp exp表示三角函数的公式:
sin ⁡ ϕ = i e − ϕ i − e ϕ i 2 cos ⁡ ϕ = e ϕ i + e − ϕ i 2 \sin \phi=i\frac{e^{-\phi i}-e^{\phi i}}{2}\\ \cos \phi=\frac{e^{\phi i}+e^{-\phi i}}{2} sinϕ=i2eϕieϕicosϕ=2eϕi+eϕi

然后我们知道 i i i在模一个NTT模数 p p p意义下为 g p − 1 4 g^{\frac{p-1}{4}} g4p1,其中 g g g为一个原根.

剩下就是套板子的事情了,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

注意在实现的时候,可以先求出 e ϕ i e^{\phi i} eϕi,再通过求逆得到 e − ϕ i e^{-\phi i} eϕi,比直接用调用两次 exp ⁡ \exp exp常数小一些.

代码如下:

void Poly_sin(int *a,int n,int *res){
  int im=wn[0][N>>2];
  for (int i=0;i<=n;++i) res[i]=mul(im,a[i]);
  Poly_exp(res,n,tc);
  for (int i=0;i<=n;++i) res[i]=0;
  Poly_inv(tc,n,res);
  for (int i=0;i<=n;++i) res[i]=mul(mul(inv2,im),sub(res[i],tc[i])),tc[i]=0;
}

void Poly_cos(int *a,int n,int *res){
  int im=wn[0][N>>2];
  for (int i=0;i<=n;++i) res[i]=mul(im,a[i]);
  Poly_exp(res,n,tc);
  for (int i=0;i<=n;++i) res[i]=0;
  Poly_inv(tc,n,res);
  for (int i=0;i<=n;++i) res[i]=mul(inv2,add(res[i],tc[i])),tc[i]=0;
}

你可能感兴趣的:(算法入门)