-
不妨又學習了一下多項式的求ln、exp、快速冪和開方操作。
-
這些操作比之前的求逆更上了一層臺階,應用同樣很廣。
多項式ln
-
給出一個次數界爲 的多項式 ,要求多項式 滿足:
-
我們對其兩邊求導(右邊相當於是複合函數求導):
-
再兩邊積分,即:
-
於是應用一下多項式求逆即可在 的時間內求出一個多項式的 ln 了。
-
其中求導和積分都是 ,具體來說就是:
-
逐項操作即可求導、積分。
多項式exp
-
給出一個次數界爲 的多項式 ,要求多項式 滿足:
-
這個需要用到 牛頓迭代法——一種在實數域和複數域上近似求解方程的方法。
-
牛頓迭代法的一般式如下(要求的根爲 ):
-
於是我們先將定義式移項:
-
兩邊取對數:
-
把 當做要求的方程的根,則根據牛頓迭代法可得:
-
這樣每次迭代都使精度(次數界)翻倍,遞歸處理即可,每次遞歸過程中要進行一次多項式求ln 。
-
這樣的時間複雜度是 的,不過常數較大。
多項式快速冪
-
給出一個次數界爲 的多項式 ,要求多項式 滿足:
-
如果像普通一樣每次直接 NTT 的話,複雜度會很不理想,特別是當 大到一定程度的時候。
-
我們可以兩邊取對數,則有:
-
右邊多項式係數直接乘 ,再兩邊exp還原即可。
-
這樣做的話 的值可以很大,我們只需要知道它模意義下的值即可。
-
這樣多項式快速冪需要用到求ln、exp,但複雜度仍是 的。
-
附模板題和我的代碼: 洛谷 P5245 【模板】多項式快速冪
-
值得注意的是這裏的 達到了 數量級,我們用高精度除單精度求其模意義下的值即可。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5,L=N<<2,G=3,mo=998244353;
int a[L],b[L],c[L];
int f[L],rev[L],wn[L],inv[L];
char s[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int ksm(int x,int y)
{
int s=1;
while(y)
{
if(y&1) s=(LL)s*x%mo;
x=(LL)x*x%mo;
y>>=1;
}
return s;
}
inline void NTT(int *y,int len,int ff)
{
for(int i=0;i<len;i++)
if(i<rev[i]) swap(y[i],y[rev[i]]);
for(int h=2,d=len>>1;h<=len;h<<=1,d>>=1)
for(int i=0,k=h>>1;i<len;i+=h)
for(int j=0,cnt=0;j<k;j++,cnt+=d)
{
int u=y[i+j],t=(LL)y[i+j+k]*wn[cnt]%mo;
y[i+j]=u+t>=mo?u+t-mo:u+t;
y[i+j+k]=u-t<0?u-t+mo:u-t;
}
if(ff==-1)
{
reverse(y+1,y+len);
int invl=inv[len];
for(int i=0;i<len;i++) y[i]=(LL)y[i]*invl%mo;
}
}
void getinv(int len,int num)
{
if(len==1)
{
b[0]=ksm(c[0],mo-2);
return;
}
getinv(len>>1,num-1);
for(int i=0;i<len>>1;i++) f[i]=c[i];
for(int i=len>>1;i<len;i++) f[i]=0;
for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<num-1;
int w0=ksm(G,(mo-1)/len);
for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;
NTT(f,len,1),NTT(b,len,1);
for(int i=0;i<len;i++) b[i]=(2-(LL)b[i]*f[i]%mo+mo)*b[i]%mo;
NTT(b,len,-1);
for(int i=len>>1;i<len;i++) b[i]=0;
}
inline void getln(int len,int num)
{
getinv(len,num);
for(int i=0;i<len-1;i++) f[i]=(LL)c[i+1]*(i+1)%mo;
f[len-1]=0;
for(int i=len>>1;i<len;i++) f[i]=b[i]=0;
NTT(f,len,1),NTT(b,len,1);
for(int i=0;i<len;i++) f[i]=(LL)f[i]*b[i]%mo;
NTT(f,len,-1);
for(int i=len-1;i>0;i--) f[i]=(LL)f[i-1]*inv[i]%mo;
f[0]=0;
}
void getexp(int len,int num)
{
if(len==1)
{
c[0]=1;
return;
}
getexp(len>>1,num-1);
for(int i=0;i<len;i++) f[i]=b[i]=0;
getln(len,num);
f[0]=(a[0]+1-f[0]+mo)%mo;
for(int i=1;i<len>>1;i++) f[i]=(a[i]-f[i]+mo)%mo;
for(int i=len>>1;i<len;i++) f[i]=0;
NTT(c,len,1),NTT(f,len,1);
for(int i=0;i<len;i++) c[i]=(LL)c[i]*f[i]%mo;
NTT(c,len,-1);
for(int i=len>>1;i<len;i++) c[i]=0;
}
int main()
{
int n=read(),k=0;
scanf("%s",s+1);
int m=strlen(s+1);
for(int i=1;i<=m;i++) k=((LL)k*10+s[i]-'0')%mo;
for(int i=0;i<n;i++) c[i]=read();
inv[0]=inv[1]=1;
for(int i=2;i<L;i++) inv[i]=(LL)(mo-mo/i)*inv[mo%i]%mo;
int len=1,num=0;
while(len<n<<1) len<<=1,num++;
getln(len,num);
for(int i=0;i<n;i++) a[i]=(LL)f[i]*k%mo;
for(int i=0;i<len;i++) f[i]=b[i]=c[i]=0;
getexp(len,num);
for(int i=0;i<n;i++) printf("%d ",c[i]);
return 0;
}