多项式exp & 牛顿迭代

牛顿迭代解决的是这样一个问题:已知 \(g(f(x))\equiv 0\pmod {x^n}\)\(g(x)\),求 模 \(x^n\) 意义下的 \(f(x)\)
这个问题可以用倍增的方式解决。首先假设你知道了 \(g(f(x))=0\) 的常数项(一般都能很方便的知道)。
然后,我们假设 \(f_0(x)\) 是模 \(x^{\lceil\frac{n}{2}\rceil}\) 意义下的答案,这个是已经求出来了的答案。
\(f(x)\)\(f_0(x)\) 的位置上进行泰勒展开(附赠通用展开公式),得到 \(\sum_{i=0}^{\infty} \frac{g^{(i)}(f_0(x))}{i!}(f(x)-f_0(x))^i\equiv 0\pmod {x^n}\),其中 \(f^{(i)}\) 指的是对函数 \(f(x)\) 进行 \(i\) 次求导。
考虑到 \(f(x)-f_0(x)\)\(\lceil\frac{n}{2}\rceil\) 之前的位置上(不包含)一定是没有值的(因为是取模意义下的,所以 \(f(x)\)\(f_0(x)\) 在这些项上的值一定相同),所以对于所有 \(2\le i\)\((f(x)-f_0(x))^i\equiv 0\pmod{x^n}\),然后上面那一大坨求和式就只会保留前两项,也就是 \(g(f_0(x))-g'(f_0(x))(f(x)-f_0(x))\equiv 0\pmod{x^n}\),然后把这个东西解掉就可以得到 \(f(x)=f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))}\pmod{x^n}\),然后这个东西就可以在倍增里直接做了!

然后考虑这个东西怎么去实现多项式 \(exp\)
首先我们看到模板题,假设给定的多项式是 \(h(x)\),那么两边同时取 \(\ln\) 就有 \(g(f(x))=\ln f_0(x)-h(x)\),求一次导之后就是 \(\frac{1}{f_0(x)}\),然后我们把这个东西带入到上面的公式里就有 \(f(x)=f_0(x)\times (1-\ln f_0(x)+h(x))\),然后递归处理即可。

Code Here
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5,mod=998244353,g=3,gi=332748118;
int n,rev[N<<2];
int qpow(int a,int b){
	int ans=1,base=a;
	while(b){
		if(b&1)ans=ans*base%mod;
		base=base*base%mod;
		b>>=1; 
	}
	return ans;
}
#define Poly vector<int>
Poly operator+(Poly x,Poly y){
	Poly z;
	z.resize(max(x.size(),y.size()));
	x.resize(max(x.size(),y.size()));y.resize(max(x.size(),y.size()));
	for(int i=0;i<z.size();++i)z[i]=(x[i]+y[i])%mod;
	return z;
}
Poly operator-(Poly x,Poly y){
	Poly z;
	z.resize(max(x.size(),y.size()));
	x.resize(max(x.size(),y.size()));y.resize(max(x.size(),y.size()));
	for(int i=0;i<z.size();++i)z[i]=(x[i]-y[i]+mod)%mod;
	return z;
}
void NTT(Poly &A,int n,int opt=1){
	A.resize(n+1);
	for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
	for(int mid=1;mid<n;mid<<=1){
		int wn=qpow(opt==1?g:gi,(mod-1)/(mid<<1));
		for(int i=0;i<n;i+=(mid<<1)){
			int w=1;
			for(int j=0;j<mid;++j,w=(w*wn)%mod){
				int tmp0=A[i+j],tmp1=w*A[i+j+mid]%mod;
				A[i+j]=(tmp0+tmp1)%mod;
				A[i+j+mid]=(tmp0-tmp1)%mod;
			}
		}
	}
} 
Poly operator*(Poly x,Poly y){
	rev[0]=0;
	int lim=1,L=0;
	while(lim<=x.size()+y.size()+3)lim<<=1ll,L++;
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
	x.resize(lim+1);y.resize(lim+1);
	NTT(x,lim,1);NTT(y,lim,1);
	for(int i=0;i<lim;++i)x[i]=x[i]*y[i]%mod;
	NTT(x,lim,-1);
	x.resize(lim+1);
	int inv=qpow(lim,mod-2);
	for(int i=0;i<lim;++i)x[i]=(x[i]*inv%mod+mod)%mod;
	return x;
}
Poly _(Poly x,int w){
	for(int i=0;i<x.size();++i)x[i]=(x[i]*w)%mod;
	return x; 
} 
Poly Inv(Poly x,int n){
	x.resize(n+1);
	if(n==1){
//		cout<<"Check: "<<x[0]<<' '<<qpow(x[0],mod-2)<<'\n';
		Poly o;
		o.push_back(qpow(x[0],mod-2));
		return o;
	}
	Poly tmp=Inv(x,n>>1);
	tmp.resize(n+1); 
//	cout<<"Check2: "<<n<<' '<<tmp.size()<<' '<<tmp[0]<<'\n';
	tmp=_(tmp,2)-x*tmp*tmp;
	tmp.resize(n+1);
	return tmp;
}
Poly inv(Poly x){
	int n=x.size();
	int lim=1;
	while(lim<=n)lim<<=1;
	x.resize(lim+1);
	Poly ans=Inv(x,lim);
	ans.resize(lim+1); 
	return ans;
}
Poly Derive(Poly x){
	Poly ans;
	for(int i=1;i<x.size();++i)ans.push_back(i*x[i]%mod);
	int n=x.size();
	int lim=1;
	while(lim<=n)lim<<=1;
	ans.resize(lim+1);
	return ans;
}
Poly Integral(Poly x){
	Poly ans;
	ans.push_back(0);
	for(int i=0;i<x.size();++i)ans.push_back(x[i]*qpow(i+1,mod-2)%mod);
	int n=x.size();
	int lim=1;
	while(lim<=n)lim<<=1;
	ans.resize(lim+1);
	return ans;
}
Poly ln(Poly x){
	Poly ans=Integral(Derive(x)*inv(x));
	return ans;
}
Poly E(Poly x,int n){
	x.resize(n+1);
	if(n==1){
		Poly now;
		now.push_back(1);
		return now;
	}
	Poly tmp=E(x,n+1>>1);
	tmp.resize(n+1);
	Poly tmp2=x-ln(tmp);
	tmp2[0]=(tmp2[0]+1)%mod;
	tmp2.resize(n+1);
	Poly ans=tmp*tmp2;
	ans.resize(n+1);
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	Poly a;
	int n,lim=1;
	cin>>n;
	while(lim<=n)lim<<=1;
	a.resize(lim+1);
	for(int i=0;i<n;++i)cin>>a[i];
	a=E(a,n+1);
	for(int i=0;i<n;++i)cout<<a[i]<<' ';
	return 0;
} 

这个东西基本上也是我目前的多项式全家桶
特别注意一下乘法要把两个多项式 resize 成接近的大小,然后 rev 尽可能开大一点,不然很容易 RE

posted @ 2023-12-27 17:44  Forever1507  阅读(32)  评论(0编辑  收藏  举报