1.求逆
設要對多項式A求逆,逆爲B:
求在模ceil(n/2)的逆,將原式與之相減後平方,發現可得在模n意義下也是0了,然後兩邊同乘A,移項即可倍增了(注意此時A也是在模當前長度意義下的)。
最後倍增形式爲B'=2*B-B*B*A (b’等於2b減b方a),邊界爲常數取逆元,也可發現多項式有無逆取決於常數有無逆元
2.開根
與求逆類似,先求ceil(n/2),然後將A移到左邊使等式右邊變成0,平方,加上4b^2a,再把右邊b^2除到左邊就發現ok了。
最終倍增形式:B'=(A+B*B)/2B(B'等於A加B方後除以2B),除法就上求逆,邊界是常數開根,暴力枚舉(並不會二次剩餘...)?模板題保證是1...
(開根裏面套求逆,雖說複雜度仍然是nlog,但已經比大部分兩個log慢了。。。)
3.Ln
兩邊求導得到所求的導數B’=A'/A,再積分即可。
4.Exp
B=e^(A)
根據牛頓迭代的那套理論,設一個函數C,使C(B)=0,那麼C(B)=LnB-A
假設已知在mod x^n的B0,要求在mod x^2n的B1
有C(B)=C(B0)/0!+C'(B0)/1!*(B-B0)+...
發現...可以省略,因爲在mod x^n下B是唯一確定的,而mod x^2n下一定也滿足mod x^n下,所以在mod x^2n時後面低的n位與mod x^n相等,當求的導數大於等於二階發現(B-B0)那一項的冪次大於等於2,也就是最小的x項冪次大於等於2n,省略即可。
最終推得
B1=B0-C(B0)/C'(B0)
而C求導時是在把B0這個多項式看成變量求導(不要把每一項的x看成變量),A是已知多項式要看成一個常數
故B1=B0(1-Ln(B0)+A)
倍增即可。(注意能Exp一定要滿足0次項爲0,不然無法賦初值)
先貼4個的代碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
const int N=1e5+100;
void Ad(int &x,int y)
{if((x+=y)>=mod)x-=mod;}
void Mul(int &x,int y)
{x=1LL*x*y%mod;}
int qpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=1LL*res*x%mod;
x=1LL*x*x%mod,y>>=1;
}
return res;
}
namespace Poly{
vector<int>A,B,C;
int wh[N*3],cc,len,n,inv[N*3];
void Pre_Ntt(int l)
{
cc=0,len=1;
while(len<=l)++cc,len<<=1;
}
void Cal_wh()
{for(int i=1;i<len;i++)wh[i]=(wh[i>>1]>>1)|((i&1)<<(cc-1));}
void Ntt(vector<int>&a,bool inv)
{
for(int i=0;i<len;i++)
if(i<wh[i])swap(a[i],a[wh[i]]);
int tp,mo,ha;
for(int l=2,md;l<=len;l<<=1)
{
md=l>>1,tp=qpow(3,(mod-1)/l);
for(int i=0;i<len;i+=l)
{
mo=1;
for(int j=0;j<md;j++,mo=1LL*mo*tp%mod)
{
ha=1LL*mo*a[i+j+md]%mod;
a[i+j+md]=(a[i+j]-ha+mod)%mod;
a[i+j]=(a[i+j]+ha)%mod;
}
}
}
if(inv)
{
tp=qpow(len,mod-2);
for(int i=1;i<len/2;i++)swap(a[i],a[len-i]);
for(int i=0;i<len;i++)Mul(a[i],tp);
}
}
void Get_Inv(vector<int>&b,int n)
{
if(n==1){b[0]=qpow(A[0],mod-2);return;}
int md=(n+1)/2;
vector<int>a,c;
Pre_Ntt(n+md+md),a.resize(len),c.resize(len);
Get_Inv(c,md);
Pre_Ntt(n+md+md),Cal_wh();
for(int i=0;i<n;i++)a[i]=A[i];
Ntt(a,0),Ntt(c,0);
for(int i=0;i<len;i++)a[i]=(2*c[i]-1LL*c[i]*c[i]%mod*a[i]%mod+mod)%mod;
Ntt(a,1);
for(int i=0;i<n;i++)b[i]=a[i];
}
void Inv(vector<int> &a,vector<int> &res,int n)
{
A=a,res.clear(),res.resize(n);
Get_Inv(res,n);
}
void Get_Sqrt(vector<int>&b,int n)
{
if(n==1){b[0]=1;return;}
int md=(n+1)/2;
vector<int>a,c;
Pre_Ntt(n+n),a.resize(len),c.resize(len);
Get_Sqrt(c,md);
for(int i=0;i<n;i++)b[i]=2*c[i]%mod;
Inv(b,b,n);
Pre_Ntt(n+n),Cal_wh();
Ntt(c,0);
for(int i=0;i<len;i++)c[i]=1LL*c[i]*c[i]%mod;
Ntt(c,1);
for(int i=0;i<n;i++)a[i]=(B[i]+c[i])%mod;
Ntt(a,0),Ntt(b,0);
for(int i=0;i<len;i++)b[i]=1LL*b[i]*a[i]%mod;
Ntt(b,1);
}
void Sqrt(vector<int> &a,vector<int> &res,int n)
{
Pre_Ntt(n+n);
B=a,res.clear(),res.resize(len);
Get_Sqrt(res,n);
}
void Dao(vector<int> &a,vector<int> &res,int n)
{
res.clear(),res.resize(n);
for(int i=1;i<n;i++)
res[i-1]=1LL*a[i]*i%mod;
res[n-1]=0;
}
void Jf(vector<int> &a,vector<int> &res,int n)
{
res.clear(),res.resize(n+1),inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
for(int i=n-1;i>=0;i--)
res[i+1]=1LL*a[i]*inv[i+1]%mod;
res[0]=0;
}
void Ln(vector<int>&a,vector<int> &res,int n)
{
static vector<int>mo,ha;
Dao(a,mo,n),Inv(a,ha,n);
Pre_Ntt(n+n),Cal_wh();
mo.resize(len),Ntt(mo,0);
ha.resize(len),Ntt(ha,0);
for(int i=0;i<len;i++)mo[i]=1LL*mo[i]*ha[i]%mod;
Ntt(mo,1),Jf(mo,res,n);
}
void Get_Exp(vector<int>&b1,int n)
{
if(n==1){b1[0]=1;return;}
int md=(n+1)/2;
vector<int>b0;
b0.resize(n),b1.resize(n);
Get_Exp(b0,md),Ln(b0,b1,n);
for(int i=0;i<n;i++)b1[i]=((i==0)-b1[i]+C[i]+mod)%mod;
Pre_Ntt(n+n),b0.resize(len),b1.resize(len);
Cal_wh(),Ntt(b0,0),Ntt(b1,0);
for(int i=0;i<len;i++)
b1[i]=1LL*b0[i]*b1[i]%mod;
Ntt(b1,1);
}
void Exp(vector<int>&a,vector<int> &res,int n)
{C=a,res.resize(n),Get_Exp(res,n);}
}
int main()
{
int n;
scanf("%d",&n);
vector<int>a,b;
a.resize(n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
Poly::Exp(a,b,n);
for(int i=0;i<n;i++)
printf("%d ",b[i]);
puts("");
}