2019.03.18【洛谷P4726】【模板】多项式指数函数/多项式Exp(NTT)(微积分初步)(泰勒展开)(牛顿迭代)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88647215

传送门


解析:

首先您需要会泰勒展开和牛顿迭代,或者您可以看接下来博主的口胡。

前置知识一、泰勒展开

其实泰勒展开的核心思想就是对于一些连续且在 x 0 x_0 处可导的函数,用一系列多项式函数去在 x 0 x_0 处无限逼近原函数,换句话说,就是构造一个在 x 0 x_0 处和原来的函数一模一样的函数。

什么叫做一模一样?我们认为,函数值相同,一阶导数,二阶导数直到 n n 阶导数全部相同,我们就认为这两个函数相同。

由此,我们得到函数 f ( x ) f(x) x = x 0 x=x_0 处的泰勒展开式: f ( x ) = f ( x 0 ) + f ( x ) ( x x 0 ) + f ( x ) ( x x 0 ) 2 + f(x)=f(x_0)+f^\prime(x)(x-x_0)+f^{\prime\prime}(x)(x-x_0)^2+…

这个函数可以一直写下去,只要在无限项处导数不为 0 0

关于泰勒展开中余项的讨论,已经超出了本文的讨论范围,在此不作阐述。

现在我们试图弄出 G ( x ) = exp ( x ) G(x)=\exp(x) x = 0 x=0 处的泰勒展开。

A ( x ) A(x) 是多项式函数且 A ( x ) G ( x ) ( m o d x n + 1 ) A(x)\equiv G(x)\pmod{x^{n+1}}

现在直接求 n n 阶导数得到: n ! a n = e x n!a_n=e^x

x = 0 x=0 得到 a n = 1 n ! a_n=\frac {1}{n!}

倒回去一路推导可以得到 a i = 1 i ! a_i=\frac{1}{i!}

由此我们得到 exp \exp 的泰勒展开式 exp ( x ) = 1 + x + x 2 2 ! + x 3 3 ! + x 4 4 ! + \exp(x)=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+\frac{x^4}{4!}+…

关于 sin , cos , tan \sin,\cos,\tan 等三角函数也可以进行泰勒展开,不过已经超出了本文的讨论范围,不作阐述。

前置知识二、牛顿迭代

普通牛顿迭代一般用于求函数零点,且逼近速度极快。

比如对函数 f ( x ) f(x) 求它的零点。

我们随机选择一个点 x 0 x_0 ,从这个点开始迭代。

求出 f ( x ) f(x) 在该点的泰勒展开式: f ( x ) = f ( x 0 ) + f ( x ) ( x x 0 ) + f(x)=f(x_0)+f^\prime(x)(x-x_0)+…

我们只保留前两项 f ( x ) = f ( x 0 ) + f ( x ) ( x x 0 ) + o ( 2 ) f(x)=f(x_0)+f^\prime(x)(x-x_0)+o(2)

然后我们需要零点 f ( x 0 ) + f ( x 0 ) ( x x 0 ) = 0 f(x_0)+f^\prime(x_0)(x-x_0)=0

得到 x = x 0 f ( x 0 ) f ( x 0 ) x=x_0-\frac{f^\prime(x_0)}{f(x_0)}

能够迅速逼近函数的零点,当然,也可以用于求函数极值,即求导数零点。

当然函数里面传进去的变量不一定只是数,也可以是未知多项式。

Solve:

现在我们需要的就是解这样一个多项式: B ( x ) = e A ( x ) B(x)=e^{A(x)}

取对数 ln ( B ( x ) ) = A ( x ) \ln(B(x))=A(x) 。设函数: f ( B ( x ) ) = ln ( B ( x ) ) A ( x ) f(B(x))=\ln(B(x))-A(x)

现在我们需要求 f ( B ( x ) ) f(B(x)) 的零点 B ( x ) B(x) 。从 m o d    x \mod x 开始倍增迭代

假设我们已经求出了 ln ( B 0 ( x ) ) A ( x ) 0 ( m o d n 2 ) \ln(B_0(x))-A(x)\equiv 0\pmod{\frac{n}{2}}

直接上迭代公式得到 B ( x ) B 0 ( x ) ( 1 ln ( B 0 ( x ) ) + A ( x ) ) ( m o d x n ) B(x)\equiv B_0(x)(1-\ln(B_0(x))+A(x))\pmod {x^n}


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int quickpow(int a,int b,int res=1){
	while(b){
		if(b&1)res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}


cs int N=410000;
int inv[N],r[N];
inline void NTT(int *A,int n,int typ){
	for(int re i=0;i<n;++i)if(i<r[i])swap(A[i],A[r[i]]);
	for(int re i=1;i<n;i<<=1){
		int wn=quickpow(typ==-1?(mod+1)/3:3,(mod-1)/i/2);
		for(int re j=0;j<n;j+=i<<1)
		for(int re k=0,x,y,w=1;k<i;++k,w=mul(w,wn)){
			x=A[j+k],y=mul(w,A[j+k+i]);
			A[j+k]=add(x,y);
			A[j+k+i]=dec(x,y);
		}
	}
	if(typ==-1)for(int re i=0;i<n;++i)A[i]=mul(A[i],inv[n]);
}

inline void deriv(int *A,int *B,int len){
	for(int re i=1;i<len;++i)B[i-1]=mul(i,A[i]);B[len-1]=0;
}

inline void integ(int *A,int *B,int len){
	for(int re i=1;i<len;++i)B[i]=mul(inv[i],A[i-1]);B[0]=0;
}

inline void Inv(int deg ,int *a,int *b){
	static int c[N];
	if(deg==1)return (void)(b[0]=quickpow(a[0],mod-2));
	Inv((deg+1)>>1,a,b);
	int re len=1;
	while(len<=deg)len<<=1;
	for(int re i=0;i<len;++i)r[i]=(r[i>>1]>>1)|((i&1)*(len>>1));
	memcpy(c,a,sizeof(int)*deg);
	memset(c+deg,0,sizeof(int)*(len-deg));
	NTT(c,len,1),NTT(b,len,1);
	for(int re i=0;i<=len;++i)b[i]=mul(b[i],dec(2,mul(b[i],c[i])));
	NTT(b,len,-1);
	memset(b+deg,0,sizeof(int)*(len-deg));
}

inline void Ln(int deg,int *f,int *g){
	static int a[N],b[N];
	deriv(f,a,deg);
	memset(b,0,sizeof(int)*deg);
	Inv(deg,f,b);
	int re len=1;
	while(len<(deg<<1))len<<=1;
	for(int re i=0;i<len;++i)r[i]=(r[i>>1]>>1)|((i&1)*(len>>1));
	memset(a+deg,0,sizeof(int)*(len-deg));
	NTT(a,len,1),NTT(b,len,1);
	for(int re i=0;i<len;++i)a[i]=mul(a[i],b[i]);
	NTT(a,len,-1);
	integ(a,g,len);
	memset(g+deg,0,sizeof(int)*(len-deg));
}

inline void Exp(int deg,int *a,int *b){
	if(deg==1){b[0]=1;return ;}
	static int F[N];
	Exp((deg+1)>>1,a,b);
	Ln(deg,b,F);
	F[0]=dec(a[0]+1,F[0]);
	for(int re i=1;i<deg;++i)F[i]=dec(a[i],F[i]);
	int re len=1;
	while(len<=deg)len<<=1;
	for(int re i=0;i<len;++i)r[i]=(r[i>>1]>>1)|((i&1)*(len>>1));
	memset(F+deg,0,sizeof(int)*(len-deg));
	NTT(F,len,1),NTT(b,len,1);
	for(int re i=0;i<len;++i)b[i]=mul(b[i],F[i]);
	NTT(b,len,-1);
	memset(b+deg,0,sizeof(int)*(len-deg));
	memset(F+deg,0,sizeof(int)*(len-deg));
}

inline void init_inv(){
	inv[0]=inv[1]=1;
	for(int re i=2;i<N;++i)inv[i]=mul(inv[mod%i],mod-mod/i);
}

int n;
int A[N],B[N]; 
signed main(){
	init_inv();
	n=getint();
	for(int re i=0;i<n;++i)A[i]=getint();
	int re len=1;
	while(len<=n)len<<=1;
	Exp(len,A,B);
	for(int re i=0;i<n;++i)cout<<B[i]<<" ";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88647215