Luogu P4725 【模板】多项式对数函数(多项式 ln)

多项式全家桶

运算法则 算法 时间复杂度
多项式乘法 快速傅里叶变换 Θ ( 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 次多项式 A A A,求多项式 B B B,满足
B ( x ) ≡ ln ⁡ A ( x ) ( m o d x n ) B(x) \equiv \ln A(x)\pmod{x^n} B(x)lnA(x)(modxn)

%   结果对 998244353 \texttt{998244353} 998244353 取模。
  
  数据范围  n ⩽ 1 0 5 n \leqslant 10^5 n105

题解

%   一般地,对于函数 F ( x ) = x a F(x)=x^a F(x)=xa,其导函数为
F ′ ( x ) = a x a − 1 F'(x)=ax^{a-1} F(x)=axa1

%   一般地,对于函数 F ′ ( x ) = x a F'(x)=x^a F(x)=xa,其不定积分为
∫ x a d x = 1 a + 1 x a + 1 = F ( x ) \int x^a\text{d}x=\frac1{a+1}x^{a+1}=F(x) xadx=a+11xa+1=F(x)  简而言之,积分和求导互为逆运算。
  令 G ( x ) ≡ ln ⁡ x ( m o d x n ) G(x)≡\ln x\pmod{x^n} G(x)lnx(modxn)  则 B ( x ) ≡ G ( A ( x ) ) ( m o d x n ) B(x)≡G(A(x))\pmod{x^n} B(x)G(A(x))(modxn)  两边求导,根据复合函数求导法则,得
B ′ ( x ) ≡ G ′ ( A ( x ) ) A ′ ( x ) ≡ A ′ ( x ) A ( x ) ≡ A ′ ( x ) A − 1 ( x ) ( m o d x n ) \begin{aligned}B'(x)&≡G'(A(x))A'(x)≡\frac{A'(x)}{A(x)}≡{A'(x)}A^{-1}(x)\pmod{x^n}\\ \end{aligned} B(x)G(A(x))A(x)A(x)A(x)A(x)A1(x)(modxn)  求逆后积分回去就好。

代码

外部封装式

#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+10){
    	t=(ll*)malloc((n+10)*sizeof (ll));
    	memset(t,0,(n+10)*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];
}
void dao(int n,poly &A,poly &B){
    for(int i=1;i<n;i++)
		B[i-1]=1ll*i*A[i]%mod;
	B[n-1]=0;
}
void summa(int n,poly &a,poly &b){
    for(int i=1;i<n;i++)
		b[i]=1ll*a[i-1]*inv(i)%mod;
	b[0]=0;
}
void ln(int n,poly &f,poly &g){
	poly A,invs;
	dao(n,f,A);inv(n,f,invs);
    calc(n,n);NTT(A);NTT(invs);
    for(int i=0;i<limit;i++)
		A[i]=A[i]*invs[i]%mod;
    NTT(A,true);summa(n,A,g);
}
poly x,y;
int n;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld",&x[i]),x[i]%=mod;
    ln(n,x,y);
    for(int i=0;i<n;i++)
        printf("%lld ",y[i]%mod);
    return 0;
}

内部封装式

#include
using namespace std;
#define ll long long
#define MAXN 4000010
const ll mod=998244353;
ll ksm(ll a,ll b){
	ll ret=1;
	while(b){
		if(b&1) ret=ret*a%mod;
		a=a*a%mod; b>>=1;
	} return ret;
}
#define invt(x) (ksm(x,mod-2))
const ll G=3,inG=invt(G);
struct poly{
	ll *t;
	int limit,inmit,l;
	poly(int n=0):limit(1),l(0),inmit(0),t(NULL){init(n);}
	~poly(){
		if(t) free(t);
		limit=l=inmit=0;
	}
	ll& operator[](int x){return t[x];}
	const ll& operator[](int x)const{return t[x];}
	void init(int n){
		if(n<=limit&&limit!=1) return;
		if(t) free(t);
		while(limit<n) limit<<=1,++l;
		inmit=invt(limit);
		t=(ll*)calloc(limit,sizeof(ll));
	}
	static const int maxn=MAXN;
	void fnt(int type){
		static int now=0,r[maxn];
		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){
			ll wn=ksm(type==1?G:inG,(mod-1)/(mid<<1));
			for(int j=0,B=mid<<1;j<limit;j+=B){
				ll w=1;
				for(int k=0;k<mid;k++,w=w*wn%mod){
					int x=t[j+k],y=t[j+k+mid]*w%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;
	}
	poly &operator*=(poly &x){
		fnt(1);x.fnt(1);
		for(int i=0;i<limit;i++)
			t[i]=t[i]*x[i]%mod;
		fnt(-1); return *this;
	}
	void inv(int n,poly &g)const{
		g.init(n);
		if(n==1) return g[0]=invt(t[0]),void();
		int half=(n+1)>>1;
		inv(half,g);
		poly F(n+n),G(n+n);
		for(int i=0;i<n;i++) F[i]=t[i];
		for(int i=0;i<half;i++) G[i]=g[i];
		F.fnt(1);G.fnt(1);
		for(int i=0;i<G.limit;i++)
			F[i]=G[i]*((2-F[i]*G[i]%mod+mod)%mod)%mod;
		F.fnt(-1);
		for(int i=0;i<n;i++) g[i]=F[i];
	}
	void diff(poly &g)const{
		g.init(limit);
		for(int i=1;i<limit;i++)
			g[i-1]=t[i]*i%mod;
		g[limit-1]=0;
	}
	void inte(poly &g)const{
		g.init(limit);
		for(int i=1;i<limit;i++)
			g[i]=t[i-1]*invt(i)%mod;
		g[0]=0;
	}
	void ln(int n,poly &g)const{
		g.init(n);
		poly diffs(n+n+1),invs(n+n+1);
		diff(diffs);
		inv(n,invs);
		diffs*=invs;
		diffs.inte(g);
	}
}a,b;
int main(){
	int n;
	scanf("%d",&n);
	a.init(n+n+2);b.init(n+n+2);
	for(int i=0;i<n;i++)
		scanf("%lld",&a[i]);
	a.ln(n,b);
	for(int i=0;i<n;i++)
		printf("%lld ",b[i]);
	return 0;
}

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