多项式求逆学习笔记

前言:

今天学习了多项式求逆,总结一下。
这里是网上好的博客

问题:

给定一个多项式\(A(x)\),求出多项式\(B(x)\),使\(A(x)B(x) \equiv 1 \pmod{x^n}\)

解析:

考虑递推求解,假设我们已经求出\(B'(x)\),使

\[A(x)B'(x) \equiv 1 \pmod{x^{\lceil \frac{n}{2} \rceil}} \]

又:

\[A(x)B(x) \equiv 1 \pmod{x^n} \]

所以:

\[B(x)-B'(x) \equiv 0 \pmod{x^{\lceil \frac{n}{2} \rceil}} \]

将式子两边平方,有:

\[B(x)^2-2B(x)B'(x)+B'(x)^2 \equiv 0 \pmod{x^n} \]

这里说明为何模数变成了\(n\),设\(G(X)=(B(x)-B'(x))^2\),那么第\(i\)个系数\(a_i = \sum_{j=0}^{i} c_j \times c_{i-j}\),因为原来在\(0\)\({\lceil {\frac{n}{2}} \rceil}\)中系数\(c_i\)均为0,所以每一项其中必然有一项\(c_i\)为0。

那么我们就得到了递推式:

\[B(x) \equiv 2B'(x)-A(x) B'(x)^2 \pmod{x^n} \]

FFT优化乘法即可。

时间复杂度:

\[T(n)=T(n/2)+O(nlogn),T(n)=O(nlogn) \]

代码实现


#include<bits/stdc++.h>
#define N 300005
using namespace std;
const int P=998244353,G=3;
int n,lim,l,a[N],b[N],c[N],r[N];
inline int In(){
	char c=getchar(); int x=0,ft=1;
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
	for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
	return x*ft;
}
inline int power(int x,int k){
	int s=1,t=x;
	for(;k;k>>=1,t=1ll*t*t%P) if(k&1) s=1ll*s*t%P;
	return s;
}
void NTT(int *a,int op) {
    for(int i=0;i<lim;++i) if(i<r[i]) swap(a[i],a[r[i]]);
    for(int i=1;i<lim;i<<=1) {
        int Wn=power(G,(P-1)/(i<<1)); if(op<1) Wn=power(Wn,P-2);
        for(int j=0;j<lim;j+=(i<<1)) {
            int w=1;
            for(int k=0;k<i;++k,w=1ll*w*Wn%P) {
                int x=a[j+k],y=1ll*w*a[j+k+i]%P;
                a[j+k]=(x+y)%P; a[j+k+i]=(x-y+P)%P;
            }
        }
    }
    if(op==-1){
    	int inv=power(lim,P-2);
	    for(int i=0;i<lim;++i) a[i]=1ll*a[i]*inv%P;
	}
}
inline void Sol(int k){
	if(k==1){ b[0]=power(a[0],P-2); return; }
	Sol((k+1)/2);
	lim=1; l=0; while(lim<2*k) lim<<=1,++l;
	for(int i=1;i<lim;++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<k;++i) c[i]=a[i]; for(int i=k;i<lim;++i) c[i]=0;
	NTT(b,1); NTT(c,1);
	for(int i=0;i<lim;++i) b[i]=1ll*(2-1ll*c[i]*b[i]%P+P)%P*b[i]%P;
	NTT(b,-1); for(int i=k;i<lim;++i) b[i]=0;
}
int main(){
	n=In(); for(int i=0;i<n;++i) a[i]=In(); Sol(n);
	for(int i=0;i<n;++i) printf("%d%c",b[i],(i==n-1)?'\n':' ');
	return 0;
}

应用

例1.预处理伯努利数。

伯努利数的指数生成函数是:

\[\sum_{i=0}^{\infty}{ B_i \frac{x^i}{ i!} = \frac{x}{e^x-1}= \frac{x}{\sum_{i=0}^{\infty}{ \frac{x^i}{ (i+1)!}} }}$。 然后求出下面多项式的逆就可以得到伯努利数了。 例2.BZOJ3456 城市规划(权限题) 题意:给定$n$,求出$n$个顶点的简单无向连通图的个数。 解析:设$f(n)$表示$n$个顶点的简单无向连通图的个数,$g(n)$表示$n$个顶点的简单无向图的个数,那么显然有: $$g(n)=2^{C(n,2)}\qquad(1)\]

考虑\(g(n)\)的另一种计算方法,枚举1号点所在连通块的顶点个数,那么有:

\[g(n)=\sum_{i=1}^{n} C(n-1,i-1)\times f(i) \times g(n-i) \qquad (2) \]

将(1)代入(2),有:

\[2^{C(n,2)}=\sum_{i=1}^{n} C(n-1,i-1)\times f(i) \times 2^{C(n-i,2)} \]

两边同时除以\((n-1)!\),有:

\[\frac{2^{C(n,2)}}{(n-1)!}=\sum_{i=1}^{n}{\frac{f(i)}{(i-1)!} \frac{2^{C(n-i,2)}}{(n-i)!}} \]

定义:\(P(x)=\sum_{n=0}^{\infty} \frac{f(n+1)}{n!} x^n\)\(Q(x)=\sum_{n=0}^{\infty} \frac{2^{C(n,2)}}{n!} x^n\)\(G(x)=\sum_{n=0}^{\infty} \frac{2^{C(n+1,2)}}{n!} x^n\)

那么据上有:

\[G(x)=P(x)Q(x) \]

因为是求\(P(x)\)的第\(n-1\)项系数,考虑这个等式在模\(x^n\)下的意义:

\[G(x) \equiv P(x)Q(x) \pmod{x^n} \]

那么有:

\[P(x) \equiv G(x)^{-1}Q(x) \pmod{x^n} \]

多项式求逆即可。

posted @ 2019-03-11 17:32  pkh68  阅读(245)  评论(0编辑  收藏  举报