Luogu P4238 【模板】多项式求逆

多项式全家桶

运算法则 算法 时间复杂度
多项式乘法 快速傅里叶变换 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式求逆 倍增+快速数论变换 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式对数函数 求导+积分 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
多项式指数函数 泰勒展开+牛顿迭代 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)
分治FFT卷积 分治FFT/多项式求逆 Θ ( n log ⁡ 2 2 n ) / Θ ( n log ⁡ 2 n ) \Theta(n\log_2^2 n)/\Theta(n\log_2 n) Θ(nlog22n)/Θ(nlog2n)

题目大意

%   给定一个 n − 1 n-1 n1 次多项式 F F F,求 G G G,满足 F ∗ G ≡ 1 ( m o d x n ) F*G≡1\pmod{x^n} FG1(modxn)

%   结果对 998244353 998244353 998244353 取模。
%   数据范围  1 ⩽ n ⩽ 1 0 5 , 0 ⩽ a i ​ ⩽ 1 0 9 1\leqslant n\leqslant 10^5,0\leqslant a_i​\leqslant 10^9 1n105,0ai109

题解

%   首先你要清楚多项式模 x n x^{n} xn 指的是仅保留这个多项式的 0 ∼ n − 1 0\sim n-1 0n1次项。
  令 G ′ G' G 满足 F ∗ G ′ ≡ 1 ( m o d x ⌈ n 2 ⌉ ) F*G'≡1\pmod{x^{\small \lceil\frac n2\rceil}} FG1(modx2n)  则有
G ′ − G ≡ 0 ( m o d x ⌈ n 2 ⌉ ) G'-G≡0\pmod{x^{\small \lceil\frac n2\rceil}} GG0(modx2n)

%   平方,得
( G ′ − G ) 2 ≡ 0 ( m o d x n ) (G'-G)^2≡0\pmod{x^n} (GG)20(modxn)

%   展开
( G ′ ) 2 − 2 G G ′ + G 2 ≡ 0 ( m o d x n ) (G')^2-2GG'+G^2≡0\pmod{x^n} (G)22GG+G20(modxn)

%   两边同时乘 F F F,得
F ( G ′ ) 2 − 2 G ′ + G ≡ 0 ( m o d x n ) F(G')^2-2G'+G≡0\pmod{x^n} F(G)22G+G0(modxn)

%   移项
G ≡ 2 G ′ − F ( G ′ ) 2 ≡ G ′ ( 2 − F G ′ ) ( m o d x n ) G≡2G'-F(G')^2≡G'(2-FG')\pmod{x^n} G2GF(G)2G(2FG)(modxn)

%   运用 FNT \text{FNT} FNT 可以将多项式乘法的时间复杂度降低到 Θ ( n log ⁡ 2 n ) \Theta(n\log_2 n) Θ(nlog2n)。可以得到递推式
T ( n ) = T ( n 2 ) + Θ ( n log ⁡ 2 n ) T(n)=T\left(\frac n2\right)+\Theta(n\log_2n) T(n)=T(2n)+Θ(nlog2n)  根据主定理1,可得
T ( n ) = Θ ( n log ⁡ 2 n ) T(n)=\Theta(n\log_2 n) T(n)=Θ(nlog2n)  通俗地讲,它每次倍增,而非每次分治,因而时间复杂度不会累乘,而是累加。

代码

外部封装式:

#include
using namespace std;
#define ll long long
#define maxn 400010
const ll mod=(119<<23)+1,G=3,inv_G=332748118;
ll pow_t(ll a,ll b){
    ll ret=1;
    for(a%=mod;b;a=a*a%mod,b>>=1)
        if(b&1) ret=ret*a%mod;
    return ret;
} ll inv(ll x){return pow_t(x,mod-2);}
struct poly{
	private:ll q_lens;
    public:ll *t;
    poly(int n=maxn):q_lens(n){
    	t=(ll*)malloc(n*sizeof (ll));
    	memset(t,0,n*sizeof (ll));
    } ~poly(){free(t);}
    void sets(){memset(t,0,q_lens*sizeof (ll));}
    ll &operator[](const int &x){return t[x];}
};
int limit,l,r[maxn];
void calc(int n,int m){
    for(limit=1,l=0;limit<=n+m;limit<<=1,++l);
    for(int i=0;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
void NTT(poly &t,bool un=false){
    for(int i=0;i<limit;i++)
        if(i<r[i]) swap(t[i],t[r[i]]);
    for(int mid=1;mid<limit;mid<<=1){
        ll wn=pow_t((un?inv_G:G),(mod-1)/(mid<<1));
        for(int j=0;j<limit;j+=(mid<<1)){
            ll w=1;
            for(int k=0;k<mid;++k,w=w*wn%mod){
                ll x=t[j+k],y=w*t[j+k+mid]%mod;
                t[j+k]=(x+y)%mod;
                t[j+k+mid]=(x-y+mod)%mod;
            }
        }
    } ll invs=inv(limit);
    if(un) for(int i=0;i<limit;i++)
        t[i]=t[i]*invs%mod;
}
void inv(int n,poly &f,poly &g){
    if(n==1) return(void)(g[0]=inv(f[0]));
    inv((n+1)>>1,f,g); calc(n,n);
    poly a(limit),b(limit);
    for(int i=0;i<n;i++) a[i]=f[i],b[i]=g[i];
    NTT(a); NTT(b);
    for(int i=0;i<limit;i++)
        b[i]=b[i]*((2ll-a[i]*b[i]%mod+mod)%mod)%mod;
    NTT(b,true);
    for(int i=0;i<n;i++) g[i]=b[i];
}
poly x,y;
int n;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld",&x[i]),x[i]%=mod;
    inv(n,x,y);
    for(int i=0;i<n;i++)
        printf("%lld ",y[i]%mod);
    return 0;
}

内部封装式(编译时间较长)

#include
#include
#include
using namespace std;
#define MAXN 4000010
const long long mod=998244353,G=3,inG=332748118;
long long pows(long long a,long long b){
	long long ret=1;
	while(b){
		if(b&1) ret=ret*a%mod;
		b>>=1;a=a*a%mod;
	} return ret;
}
class poly{
	public:
	static const int maxn=MAXN;
	long long *t;
	int limit,l,inmit;
	long long& operator[](int x){return t[x];}
	const long long& operator[](int x)const{return t[x];}
	poly(int n=0):t(NULL),limit(1),l(0){init(n);}
	void init(int n){
		if(limit>=n) return;
		if(t) free(t);
		while(limit<n) limit<<=1,++l;
		inmit=pows(limit,mod-2);
		t=(long long*)calloc(limit+5,sizeof(long long));
	}
	void fnt(int type){
		static long long r[MAXN];
		static int now=0;
		if(now!=limit&&(now=limit))
			for(int i=1;i<limit;i++)
				r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
		for(int i=1;i<limit;i++)
			if(i<r[i]) swap(t[i],t[r[i]]);
		for(int mid=1;mid<limit;mid<<=1){
			long long wn=pows(type==1?G:inG,(mod-1)/(mid<<1));
			for(int j=0,B=(mid<<1);j<limit;j+=B){
				long long w=1;
				for(int k=0;k<mid;k++,w=w*wn%mod){
					int x=t[j+k],y=w*t[j+k+mid]%mod;
					t[j+k]=(x+y)%mod;t[j+k+mid]=(x-y+mod)%mod;
				}
			}
		} if(type==-1) for(int i=0;i<limit;i++)
			t[i]=t[i]*inmit%mod;
	}
	void operator*=(poly &x){
		fnt(1);x.fnt(1);
		for(int i=0;i<limit;i++)
			t[i]=t[i]*x[i]%mod;
		fnt(-1);
	}
	void inv(int n,poly &g)const{
		g.init(n+n);
	    if(n==1) return(void)(g[0]=pows(t[0],mod-2));
	    inv((n+1)>>1,g);
	    poly a(n+n),b(n+n);
	    for(int i=0;i<n;i++) a[i]=t[i],b[i]=g[i];
	    a.fnt(1); b.fnt(1);
	    for(int i=0;i<a.limit;i++)
	        b[i]=b[i]*((2ll-a[i]*b[i]%mod+mod)%mod)%mod;
	    b.fnt(-1);
	    for(int i=0;i<n;i++) g[i]=b[i];
	}
}a,b;
int n;
int main(){
	scanf("%d",&n);
	a.init(n);
	for(int i=0;i<n;i++)
		scanf("%lld",&a[i]);
	a.inv(n,b);
	for(int i=0;i<n;i++)
		printf("%lld ",b[i]);
	return 0;
}

  1. %   根据 T ( n ) = T ( n 2 ) + Θ ( n log ⁡ 2 n ) T(n)=T\left(\dfrac n2\right)+\Theta(n\log_2 n) T(n)=T(2n)+Θ(nlog2n)  因而 f ( n ) = Θ ( n log ⁡ 2 n ) > Θ ( n l o g 2 1 ) = Θ ( 1 ) f(n)=\Theta(n\log_2 n)>\Theta(n^{log_2 1})=\Theta(1) f(n)=Θ(nlog2n)>Θ(nlog21)=Θ(1),故 T ( n ) = f ( n ) = Θ ( n log ⁡ 2 n ) T(n)=f(n)=\Theta(n\log_2 n) T(n)=f(n)=Θ(nlog2n) ↩︎

你可能感兴趣的:(多项式,快速数论变换)