多项式整理(upd:2022.1.6)
多项式
by AmanoKumiko
1.求逆
需要\([x^0]f(x)≠0\)
\(Tips\):\(O(N^2)\)暴力求逆
\[F(x)G(x)\equiv1(mod\ x^n)\\
H(x)=F(x)G(x)\\
h_n=\sum_{i=0}^nf_ig_{n-i}=0\\
h_0=f_0g_0=1=>f_0=g_0^{-1}\\
f_n=-g_0^{-1}\sum_{i=0}^{n-1}f_ig_{n-i}
\]
2.ln
需要\([x^0]f(x)=1\)
3.Exp
需要\([x^0]f(x)=0\)
\(Tips\):\(O(N^2)\)暴力\(exp\)
\[F(x)=exp\ G(x)\\
F'(x)=exp\ G(x)G'(x)\\
xF'(x)=xF(x)G'(x)\\
nf_n=\sum_{i=1}^nig_if_{n-i}
\]
4.积化和
即将乘积先取对数再\(exp\)
1.\(-ln(1-x)=\sum_{i=1}^{+∞}\frac{x^i}{i}\)求解
2.形如\(\prod F(ix)\)的复合可以用自然数幂和求解
Problems:
5.和化积
利用\(ln\)相关导数和\([f(x)±g(x)]'=f'(x)±g'(x)\)将和化为积
Problems:
6.常用模数
1.\(119*2^{23}+1\)
2.\(224*2^{21}+1\)
3.\(479*2^{21}+1\)
7.板子
1.NTT
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 600010
#define mo 998244353
#define LL long long
#define ULL unsigned long long
int rev[N],G1[N],G2[N],fac[N],ifac[N],inv[N];
int mod(int x){return x>=mo?x-mo:x;}
int mi(int x,int y){
if(!y)return 1;
if(y==1)return x;
return y%2?1ll*x*mi(1ll*x*x%mo,y/2)%mo:mi(1ll*x*x%mo,y/2);
}
void init(){
fac[0]=ifac[0]=1;
F(i,1,N-10)fac[i]=1ll*fac[i-1]*i%mo,inv[i]=(i==1?1:1ll*mo/i*mod(mo-1ll*inv[mo%i]%mo)%mo);
ifac[N-10]=mi(fac[N-10],mo-2);
Fd(i,N-11,1)ifac[i]=1ll*ifac[i+1]*(i+1)%mo;
for(int l=1;l<=N-10;l<<=1)G1[l]=mi(3,(mo-1)/(l*2)),G2[l]=mi(G1[l],mo-2);
}
void BRT(int x){F(i,0,x-1)rev[i]=(rev[i>>1]>>1)|((i&1)?(x>>1):0);}
struct poly{
vector<int>val;
poly(int x=0){if(x)val.push_back(x);}
poly(const vector<int>&x){val=x;}
void ins(int x){val.push_back(x);}
void clear(){vector<int>().swap(val);}
int sz(){return val.size();}
void rsz(int x){val.resize(x);}
void shrink(){for(;sz()&&!val.back();val.pop_back());}
void Rev(){reverse(val.begin(),val.end());}
poly modxn(int x){
if(val.size()<=x)return poly(val);
else return poly(vector<int>(val.begin(),val.begin()+x));
}
int operator[](int x)const{
if(x<0||x>=val.size())return 0;
return val[x];
}
void NTT(int x){
static ULL f[N],w[N];
w[0]=1;
F(i,0,sz()-1)f[i]=(((LL)mo<<5)+val[rev[i]])%mo;
for(int mid=1;mid<sz();mid<<=1){
int tmp=(x==1?G1[mid]:G2[mid]);
F(i,1,mid-1)w[i]=w[i-1]*tmp%mo;
for(int i=0;i<sz();i+=(mid<<1)){
F(j,0,mid-1){
int t=w[j]*f[i|j|mid]%mo;
f[i|j|mid]=f[i|j]+mo-t;f[i|j]+=t;
}
}
//if(mid==(1<<10)){F(i,0,sz()-1)f[i]%=mo;};
}
if(x==-1){int tmp=inv[sz()];F(i,0,sz()-1)val[i]=f[i]%mo*tmp%mo;}
else{F(i,0,sz()-1)val[i]=f[i]%mo;}
}
void DFT(){NTT(1);}
void IDFT(){NTT(-1);}
friend poly operator*(poly x,poly y){
if(x.sz()<30||y.sz()<30){
if(x.sz()>y.sz())swap(x,y);
poly ret;
ret.rsz(x.sz()+y.sz());
F(i,0,ret.sz()-1){
for(int j=0;j<=i&&j<x.sz();j++)
ret.val[i]=mod(ret.val[i]+1ll*x[j]*y[i-j]%mo);
}
// ret.shrink();
return ret;
}
int l=1;
while(l<x.sz()+y.sz()-1)l<<=1;
x.rsz(l);y.rsz(l);BRT(l);
x.DFT();y.DFT();
F(i,0,l-1)x.val[i]=1ll*x[i]*y[i]%mo;
x.IDFT();
// x.shrink();
return x;
}
friend poly operator+(poly x,poly y){
poly ret;
ret.rsz(max(x.sz(),y.sz()));
F(i,0,ret.sz()-1)ret.val[i]=mod(x[i]+y[i]);
return ret;
}
friend poly operator-(poly x,poly y){
poly ret;
ret.rsz(max(x.sz(),y.sz()));
F(i,0,ret.sz()-1)ret.val[i]=mod(x[i]-y[i]+mo);
return ret;
}
poly &operator*=(poly x){return (*this)=(*this)*x;}
poly &operator+=(poly x){return (*this)=(*this)+x;}
poly &operator-=(poly x){return (*this)=(*this)-x;}
poly deriv(){
poly f;
f.rsz(sz()-1);
F(i,0,sz()-2)f.val[i]=1ll*(i+1)*val[i+1]%mo;
return f;
}
poly integ(){
poly f;
f.rsz(sz()+1);
F(i,1,sz())f.val[i]=1ll*val[i-1]*inv[i]%mo;
return f;
}
poly inver(int Len){
poly f,g,res(mi(val[0],mo-2));
for(int i=1;i<Len;){
i<<=1;f.rsz(i);g.rsz(i);BRT(i);
F(j,0,i-1)f.val[j]=(*this)[j],g.val[j]=res[j];
f.DFT();g.DFT();
F(j,0,i-1)f.val[j]=1ll*f[j]*g[j]%mo;
f.IDFT();
F(j,0,(i>>1)-1)f.val[j]=0;
f.DFT();
F(j,0,i-1)f.val[j]=1ll*f[j]*g[j]%mo;
f.IDFT();
res.rsz(i);
F(j,i>>1,i-1)res.val[j]=mod(mo-f[j]);
}
return res.modxn(Len);
}
poly Sqrt(int Len){
int i2=inv[2];
poly f,g,tmp;
f.clear();g.clear();
g.ins(1);
for(int i=1;i<Len*2;i<<=1){
int Len=i<<1;
f.rsz(Len);
tmp=g.inver(i);
tmp.rsz(Len);
BRT(Len);
F(j,0,i-1)f.val[j]=(j<val.size()?val[j]:0);
f.DFT();tmp.DFT();
F(j,0,Len-1)f.val[j]=1ll*f[j]*tmp[j]%mo;
f.IDFT();
g.rsz(i);
F(j,0,i-1)g.val[j]=1ll*i2*mod(g[j]+f[j])%mo;
}
return g.modxn(Len);
}
poly Ln(int Len){
return (deriv()*inver(Len)).integ().modxn(Len);
}
poly Exp(int Len){
poly f;
f.clear();
f.ins(1);
for(int i=2;i<Len*2;i<<=1)f=(f*(1-f.Ln(i)+modxn(i))).modxn(i);
return f.modxn(Len);
}
poly Pow(int Len,int k){
poly f;
f.clear();
int tail=0;
while(val[tail]==0&&tail<sz())tail++;
if(tail>=sz())return f;
if(tail*k>=Len)return f;
f.rsz(Len);
int Mul=mi(val[tail],mo-2);
F(i,0,min(Len-1,sz()-tail-1))f.val[i]=1ll*val[i+tail]*Mul%mo;
Mul=mi(val[tail],k);
f=f.Ln(Len);
F(i,0,Len-1)f.val[i]=1ll*f[i]*(k%mo)%mo;
f=f.Exp(Len);
Fd(i,Len-1,tail*k)f.val[i]=1ll*f[i-tail*k]*Mul%mo;
F(i,0,tail*k-1)f.val[i]=0;
return f;
}
};
struct Evaluation{
#define ls x<<1
#define rs (x<<1)|1
poly f,g[N],h[N];
int a[N],b[N],n,m;
void solve1(int x,int l,int r){
if(l==r){g[x].rsz(2);g[x].val[0]=1;g[x].val[1]=mod(mo-a[l]);return;}
int mid=l+r>>1,Len=1;
solve1(ls,l,mid);solve1(rs,mid+1,r);
if(r-l+1<=50){
g[x].rsz(r-l+2);
F(i,0,r-l+1){
ULL s=0;
F(j,0,mid-l+1)s+=1ll*g[ls][j]*g[rs][i-j]%mo;
g[x].val[i]=s%mo;
}
}else{
while(Len<r-l+1+1)Len<<=1;BRT(Len);
poly A=g[ls],B=g[rs];
A.rsz(Len);B.rsz(Len);
A.DFT();B.DFT();
F(i,0,Len-1)A.val[i]=1ll*A[i]*B[i]%mo;
A.IDFT();
g[x]=A.modxn(r-l+2);
}
}
void solve2(int x,int l,int r){
if(l==r){b[l]=h[x][0];return;}
int mid=l+r>>1,Len=1;
while(Len<(r-l+1<<1)+1)Len<<=1;BRT(Len);
if(r-l+1<=50){
h[ls].rsz(mid-l+1+1);
F(i,0,mid-l+1){
ULL s=0;
F(j,0,r-l+1-i)s+=1ll*g[rs][j]*h[x][i+j]%mo;
h[ls].val[i]=s%mo;
}
h[rs].rsz(r-mid+1);
F(i,0,r-mid){
ULL s=0;
F(j,0,r-l+1-i)s+=1ll*g[ls][j]*h[x][i+j]%mo;
h[rs].val[i]=s%mo;
}
}else{
g[ls].Rev();g[rs].Rev();
g[ls].rsz(Len);g[rs].rsz(Len);h[x].rsz(Len);
g[ls].DFT();g[rs].DFT();h[x].DFT();
F(i,0,Len-1)g[ls].val[i]=1ll*g[ls][i]*h[x][i]%mo;
F(i,0,Len-1)g[rs].val[i]=1ll*g[rs][i]*h[x][i]%mo;
g[ls].IDFT();g[rs].IDFT();
h[ls].rsz(mid-l+1+1);
F(i,0,mid-l+1)h[ls].val[i]=g[rs][i+r-mid];
h[rs].rsz(r-mid+1);
F(i,0,r-mid)h[rs].val[i]=g[ls][i+mid-l+1];
}
solve2(ls,l,mid);solve2(rs,mid+1,r);
}
void get(){
int n=f.sz()-1,m=n+1;
solve1(1,0,n);
g[1]=g[1].inver(n+1);
g[1].Rev();
int Len=1;
while(Len<(n<<1)+1)Len<<=1;BRT(Len);
g[1].rsz(Len);f.rsz(Len);
g[1].DFT();f.DFT();
F(i,0,Len-1)g[1].val[i]=1ll*g[1][i]*f[i]%mo;
g[1].IDFT();
h[1].rsz(n+1);
F(i,0,n)h[1].val[i]=g[1][i+n];
solve2(1,0,n);
}
}t;
poly f;
int n;
int main(){
init();
scanf("%d",&n);
F(i,0,n-1){
int x;
scanf("%d",&x);
f.ins(x);
}
t.f=f;
t.get();
F(i,0,n-1)f.val[i]=1ll*t.b[i]*ifac[i]%mo;
poly g;
F(i,0,n-1)g.ins((i&1)?mo-ifac[i]:ifac[i]);
f*=g;
F(i,0,n-1)printf("%d ",f[i]);
return 0;
}
2.MTT
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define mo 998244353
#define N 600010
#define LL long long
#define Ld long double
const Ld PI=acos(-1.0);
struct Complex{
Ld real,img;
Complex(Ld a=0.0,Ld b=0.0){real=a;img=b;}
Complex Conj(){return Complex(real,-img);}
friend Complex operator+(Complex a,Complex b){return Complex(a.real+b.real,a.img+b.img);}
friend Complex operator-(Complex a,Complex b){return Complex(a.real-b.real,a.img-b.img);}
friend Complex operator*(Complex a,Complex b){return Complex(a.real*b.real-a.img*b.img,a.real*b.img+a.img*b.real);}
friend Complex operator*(Complex a,Ld b){return Complex(a.real*b,a.img*b);}
friend Complex operator/(Complex a,Ld b){return Complex(a.real/b,a.img/b);}
Complex operator+=(Complex x){return (*this)=(*this)+x;}
Complex operator-=(Complex x){return (*this)=(*this)-x;}
Complex operator*=(Complex x){return (*this)=(*this)*x;}
};
Complex G[N];
const Complex I=Complex(0,1);
int rev[N];
void BRT(int x){
F(i,0,x-1)rev[i]=(rev[i>>1]>>1)|((i&1)?x>>1:0);
F(i,0,x-1)G[i]=Complex(cos(2.0*PI/x*i),sin(2.0*PI/x*i));
}
void FFT(Complex *A,int Len,int x){
F(i,0,Len-1)if(rev[i]<i)swap(A[rev[i]],A[i]);
static Complex w[N];
w[0]=Complex(1,0);
for(int mid=1;mid<Len;mid<<=1){
F(i,1,mid-1)w[i]=(x==1?G[i*(Len/(mid<<1))]:G[i*(Len/(mid<<1))].Conj());
for(int i=0;i<Len;i+=mid<<1){
Complex *omega=w;
for(int j=0;j<mid;j++,omega++){
Complex t=*omega*A[i|j|mid];
A[i|j|mid]=A[i|j]-t;A[i|j]+=t;
}
}
}
if(x==-1){F(i,0,Len-1)A[i]=A[i]/(Ld)Len;}
}
void DFT(Complex *A,int Len){FFT(A,Len,1);}
void IDFT(Complex *A,int Len){FFT(A,Len,-1);}
void MTT(int*A,int*B,int*C,int l){
vector<Complex>Pa(l),Pb(l),a0(l),a1(l),b0(l),b1(l);
F(i,0,l-1){
Pa[i]=Complex(A[i]>>15,A[i]&32767);
Pb[i]=Complex(B[i]>>15,B[i]&32767);
}
DFT(Pa.data(),l);DFT(Pb.data(),l);
F(i,0,l-1){
Complex Qa=(i?Pa[l-i].Conj():Pa[0].Conj()),Qb=(i?Pb[l-i].Conj():Pb[0].Conj());
a0[i]=(Pa[i]+Qa)*0.5;a1[i]=(Qa-Pa[i])*0.5*I;
b0[i]=(Pb[i]+Qb)*0.5;b1[i]=(Qb-Pb[i])*0.5*I;
}
F(i,0,l-1){
Pa[i]=a0[i]*b0[i]+I*a1[i]*b0[i];
Pb[i]=a0[i]*b1[i]+I*a1[i]*b1[i];
}
IDFT(Pa.data(),l);IDFT(Pb.data(),l);
F(i,0,l-1){
int na=(LL)(Pa[i].real+0.5)%mo,nb=(LL)(Pa[i].img+0.5)%mo,nc=(LL)(Pb[i].real+0.5)%mo,nd=(LL)(Pb[i].img+0.5)%mo;
C[i]=(1ll*na*(1<<30)+1ll*(nb+nc)*(1<<15)+nd)%mo;
}
}
int fac[N],ifac[N],inv[N];
int mod(int x){return x>=mo?x-mo:x;}
int mi(int x,int y){
if(!y)return 1;
if(y==1)return x;
return y%2?1ll*x*mi(1ll*x*x%mo,y/2)%mo:mi(1ll*x*x%mo,y/2);
}
void init(){
fac[0]=ifac[0]=1;
F(i,1,N-10)fac[i]=1ll*fac[i-1]*i%mo,inv[i]=(i==1?1:1ll*mo/i*mod(mo-1ll*inv[mo%i]%mo)%mo);
ifac[N-10]=mi(fac[N-10],mo-2);
Fd(i,N-11,1)ifac[i]=1ll*ifac[i+1]*(i+1)%mo;
}
struct poly_NTT{
vector<int>val;
poly_NTT(int x=0){if(x)val.push_back(x);}
poly_NTT(const vector<int>&x){val=x;}
void Rev(){reverse(val.begin(),val.end());}
void ins(int x){val.push_back(x);}
void clear(){vector<int>().swap(val);}
int sz(){return val.size();}
void rsz(int x){val.resize(x);}
void shrink(){for(;sz()&&!val.back();val.pop_back());}
poly_NTT modxn(int x){
if(val.size()<=x)return poly_NTT(val);
else return poly_NTT(vector<int>(val.begin(),val.begin()+x));
}
int operator[](int x)const{
if(x<0||x>=val.size())return 0;
return val[x];
}
friend poly_NTT operator*(poly_NTT x,poly_NTT y){
if(x.sz()<30||y.sz()<30){
if(x.sz()>y.sz())swap(x,y);
poly_NTT ret;
ret.rsz(x.sz()+y.sz());
F(i,0,ret.sz()-1){
for(int j=0;j<=i&&j<x.sz();j++)
ret.val[i]=mod(ret.val[i]+1ll*x[j]*y[i-j]%mo);
}
// ret.shrink();
return ret;
}
int l=1;
while(l<x.sz()+y.sz()-1)l<<=1;
x.rsz(l);BRT(l);
static int A[N],B[N],C[N];
F(i,0,x.sz()-1)A[i]=x[i];F(i,x.sz(),l-1)A[i]=0;
F(i,0,y.sz()-1)B[i]=y[i];F(i,y.sz(),l-1)B[i]=0;
MTT(A,B,C,l);
F(i,0,l-1)x.val[i]=C[i];
// x.shrink();
return x;
}
friend poly_NTT operator+(poly_NTT x,poly_NTT y){
poly_NTT ret;
ret.rsz(max(x.sz(),y.sz()));
F(i,0,ret.sz()-1)ret.val[i]=mod(x[i]+y[i]);
return ret;
}
friend poly_NTT operator-(poly_NTT x,poly_NTT y){
poly_NTT ret;
ret.rsz(max(x.sz(),y.sz()));
F(i,0,ret.sz()-1)ret.val[i]=mod(x[i]-y[i]+mo);
return ret;
}
poly_NTT &operator*=(poly_NTT x){return (*this)=(*this)*x;}
poly_NTT &operator+=(poly_NTT x){return (*this)=(*this)+x;}
poly_NTT &operator-=(poly_NTT x){return (*this)=(*this)-x;}
poly_NTT deriv(){
poly_NTT f;
f.rsz(sz()-1);
F(i,0,sz()-2)f.val[i]=1ll*(i+1)*val[i+1]%mo;
return f;
}
poly_NTT integ(){
poly_NTT f;
f.rsz(sz()+1);
F(i,1,sz())f.val[i]=1ll*val[i-1]*inv[i]%mo;
return f;
}
poly_NTT inver(int Len){
poly_NTT res(mi(val[0],mo-2));
for(int i=1;i<Len;){
i<<=1;
res=(res*(2-res*modxn(i))).modxn(i);
}
return res.modxn(Len);
}
poly_NTT Sqrt(int Len){
poly_NTT f,res(1);
for(int i=1;i<Len;){
i<<=1;
f=res.inver(i);
res=((res+f*modxn(i))*inv[2]).modxn(i);
}
return res.modxn(Len);
}
poly_NTT Ln(int Len){
return (deriv()*inver(Len)).integ().modxn(Len);
}
poly_NTT Exp(int Len){
poly_NTT f(1);
for(int i=2;i<Len*2;i<<=1)f=(f*(1-f.Ln(i)+modxn(i))).modxn(i);
return f.modxn(Len);
}
poly_NTT Pow(int Len,int k){
poly_NTT f;
f.clear();
int tail=0;
while(val[tail]==0&&tail<sz())tail++;
if(tail>=sz())return f;
if(tail*k>=Len)return f;
f.rsz(Len);
int Mul=mi(val[tail],mo-2);
F(i,0,min(Len-1,sz()-tail-1))f.val[i]=1ll*val[i+tail]*Mul%mo;
Mul=mi(val[tail],k);
f=f.Ln(Len);
F(i,0,Len-1)f.val[i]=1ll*f[i]*(k%mo)%mo;
f=f.Exp(Len);
Fd(i,Len-1,tail*k)f.val[i]=1ll*f[i-tail*k]*Mul%mo;
F(i,0,tail*k-1)f.val[i]=0;
return f;
}
};
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
init();
return 0;
}