981 words
5 minutes
多项式

板子#

#include<bits/stdc++.h>
#define ll long long
#define ld double
#define N 3000500
#define mod 998244353
const ld PI=acos(-1.0);
using std::string;
string s1,s2;
int read(){int p=0,flg=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-') flg=-1;c=getchar();}while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}return p*flg;}
void write(int x){if (!x)return;write(x/10);putchar(x%10+'0');}
namespace Poly {
int p[N],q[N],r[N],w[N],invnum[N],invfac[N],fac[N];
int add(int x,int y){return x+y>mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int ext(int n){return n==1?1:(1<<(std::__lg(n-1)+1));}
int qpow(int a,int b){int res=1;for (;b;b>>=1,a=1ll*a*a%mod)if (b&1)res=1ll*res*a%mod;return res;}
void get_r(int n){for (int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);}
void print(int *a,int n){for (int i=0;i<n;i++)printf("%d%c",a[i]," \n"[i==n-1]);}
void init_poly(){
for(int k=2,m=1;k<=N;k<<=1,m<<=1){
w[m]=1;
for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
}invnum[1]=1;
for (int i=2;i<N;i++)invnum[i]=1ll*(mod-mod/i)*invnum[mod%i]%mod;
invfac[0]=1;
for (int i=1;i<N;i++)invfac[i]=1ll*invfac[i-1]*invnum[i]%mod;
fac[0]=1;
for (int i=1;i<N;i++)fac[i]=1ll*fac[i-1]*i%mod;
}
void NTT(int *a,int n,int op){
for (int i=0;i<n;i++)if (i<r[i])std::swap(a[i],a[r[i]]);
for (int k=2,m=1;k<=n;m<<=1,k<<=1){
for (int i=0;i<n;i+=k)
for (int j=i,*x=w+m;j<i+m;x++,j++){
int v=1ll*a[j+m]*(*x)%mod;
a[j+m]=dec(a[j],v);a[j]=add(a[j],v);
}
}
if (op==-1){
std::reverse(a+1,a+n);
for (int i=0,v=qpow(n,mod-2);i<n;i++)a[i]=1ll*a[i]*v%mod;
}
}
void mul(int *a,int *b,int n,int m){
if (!n||!m)return;
int len=ext(n+m-1);
for (int i=0;i<len;i++)p[i]=i<n?a[i]:0,q[i]=i<m?b[i]:0;
get_r(len);
NTT(p,len,1);NTT(q,len,1);
for (int i=0;i<len;i++)a[i]=1ll*p[i]*q[i]%mod;
NTT(a,len,-1);
}
void mul_2(int *a,int *b,int *c,int n,int m){
if (!n||!m)return;
int len=ext(n+m-1);
for (int i=0;i<len;i++)p[i]=i<n?a[i]:0,q[i]=i<m?b[i]:0;
get_r(len);
NTT(p,len,1);NTT(q,len,1);
for (int i=0;i<len;i++)c[i]=1ll*p[i]*q[i]%mod;
NTT(c,len,-1);
}
void _inv(int *a,int *b,int n){
static int c[N],d[N];
n=ext(n);memset(b,0,4*n);b[0]=qpow(a[0],mod-2);
for (int k=2;k<=n;k<<=1){
for (int i=0;i<k<<1;i++)c[i]=i<k?a[i]:0,d[i]=i<k>>1?b[i]:0;
get_r(k);NTT(d,k,1);
for (int i=0;i<k;i++)d[i]=1ll*d[i]*d[i]%mod;
NTT(d,k,-1);get_r(k<<1);NTT(c,k<<1,1);NTT(d,k<<1,1);
for (int i=0;i<k<<1;i++)c[i]=1ll*c[i]*d[i]%mod;
NTT(c,k<<1,-1);
for (int i=0;i<k;i++)b[i]=(2ll*b[i]-c[i]+mod)%mod;
}
}
void _diff(int *a,int *b,int n){//a-n个数
for (int i=1;i<n;i++)b[i-1]=1ll*i*a[i]%mod;b[n-1]=0;
}
void _integ(int *a,int *b,int n){//b-n个数
for (int i=1;i<n;i++)b[i]=1ll*invnum[i]*a[i-1]%mod;b[0]=0;
}
void _ln(int *a,int *b,int n){
static int c[N],d[N];
assert(a[0]==1);
n=ext(n);
_inv(a,c,n),_diff(a,d,n),mul(c,d,n,n);_integ(c,b,n);
}
void _exp(int *a,int *b,int n){
static int c[N];
assert(a[0]==0);
n=ext(n);memset(b,0,4*n);memset(c,0,4*n);b[0]=1;
for (int k=2;k<=n;k<<=1){
_ln(b,c,k);
for (int i=0;i<k;i++)c[i]=dec(a[i],c[i]);
c[0]++;mul(b,c,k,k);
}
}
void _sqrt(int *a,int *b,int n){
static int c[N],d[N];
static const int inv2=(mod+1)>>1;
assert(a[0]==1);
n=ext(n);memset(b,0,4*n);b[0]=1;
for (int k=2;k<=n;k<<=1){
memcpy(c,a,4*k);_inv(b,d,k),mul(c,d,k,k);
for (int i=0;i<k;i++)b[i]=1ll*(b[i]+c[i])*inv2%mod;
}
}
void _pow(int *a,int *b,int n,string k){
static int c[N],d[N];
int u=0,k1=0,k2=0;
memset(b,0,4*n);
for (int i=0;i<k.size();i++)k1=(10ll*k1+k[i]-'0')%mod,k2=(10ll*k2+k[i]-'0')%(mod-1);
for (u=0;u<n&&!a[u];u++);
if ((u&&k.size()>=5)||1ll*u*k1>=n)return;
for (int i=u,x=qpow(a[u],mod-2);i<n;i++)c[i-u]=1ll*a[i]*x%mod;
_ln(c,d,n-u);
for (int i=0;i<n-u;i++)d[i]=1ll*d[i]*k1%mod;
_exp(d,c,n-u);
for (int i=u*k1,x=qpow(a[u],k2);i<n;i++)b[i]=1ll*c[i-u*k1]*x%mod;
}
void _pow_p(int *a,int *b,int n,string k){
static int c[N],d[N];
int u=0,k1=0,k2=0;
for (int i=0;i<k.size();i++)k1=(10ll*k1+k[i]-'0')%mod;
_ln(a,d,n);
for (int i=0;i<n;i++)d[i]=1ll*d[i]*k1%mod;
_exp(d,b,n);
}
void _div(int *a,int *b,int *q,int *r,int n,int m){
static int c[N],d[N],e[N];
int len=n-m+1;
for (int i=0;i<len;i++)c[i]=a[n-1-i];
for (int i=0;i<len;i++)d[i]=i<m?b[m-1-i]:0;
_inv(d,e,len);mul(c,e,len,len),std::reverse(c,c+len);
memcpy(q,c,4*len),mul(c,b,len,m);
for (int i=0;i<m;i++)r[i]=dec(a[i],c[i]);
}
void cdq(int l,int r,int *f,int *g){
static int c[N];
if (l==r)return ;
int mid=(l+r)>>1;
cdq(l,mid,f,g);mul_2(f+l,g+1,c,mid-l+1,r-l);
for (int i=mid+1;i<=r;i++)f[i]=add(f[i],c[i-l-1]);
cdq(mid+1,r,f,g);
}
}
using namespace Poly;
int n,m;int a[N],b[N],Q[N],R[N];
string k;
void solve(){
scanf("%d",&n);
for (int i=1;i<n;i++)scanf("%d",a+i);b[0]=1;
cdq(0,n-1,b,a);
print(b,n);
}
int main(){
int t=1;
init_poly();
// scanf("%d",&t);
while (t--){
solve();
}
return 0;
}

EulerEuler变换#

F(x)=ifixiF(x)=\sum_{i}f_ix^i
第一个定义式
ε(F(x))=i1(1xi)fi\varepsilon(F(x))=\prod_{i}\frac{1}{(1-x^i)^{f_i}}
EulerEuler变换的组合意义:无标号的nn个球,分成若干非空组,大小为ii的组内部具有fif_i中方案数,为最后的总方案数(如果两个组大小和内部方案相同,则他们不可区分)
注:11xi\frac{1}{1-x^i}在枚举有几个大小为ii的组,fif_i是大小为ii的内部的方案数
第二种定义式

ε(F(x))=i1(1xi)fi=exp(ln(i1(1xi)fi))=exp(ifiln(11xi)=exp(ifijxijj)=exp(jifixijj)=exp(jF(xj)j)\begin{align} \varepsilon(F(x))&=\prod_{i}\frac{1}{(1-x^i)^{f_i}}\\ &=\exp(\ln(\prod_{i}\frac{1}{(1-x^i)^{f_i}}))\\ &=\exp(\sum_{i}-f_i\ln(\frac{1}{1-x^i})\\ &=\exp(\sum_{i}-f_i\sum_j\frac{-x^{ij}}{j})\\ &=\exp(\sum_{j}\sum_if_i\frac{x^{ij}}{j})\\ &=\exp(\sum_j\frac{F(x^j)}{j})\\ \end{align}

分治FFTFFT
1.fn=t(n)i=1nfigni1.f_{n}=t(n)\sum_{i=1}^{n}f_{i}g_{n-i}
考虑分治区间[l,r][l,r]
已经计算完[l,mid][l,mid]
此时[l,mid][l,mid]ff值是已知的
又因为,在递归[mid+1,r][mid+1,r]时,需保证[1,mid][1,mid]的贡献已经计算
也就是说我们需要处理[l,mid][l,mid]ff[mid+1,r][mid+1,r]ff的贡献
ff的下标是[l,mid][l,mid],由于要覆盖[mid+1,r][mid+1,r],所以gg的下标是[1,rl][1,r-l]
fl,,mid×g1,,rlfmid+1,,rf_{l,\dots,mid}\times g_{1,\dots,r-l}\to f_{mid+1,\dots,r}
对于t(n)t(n),我们保证转移的时候ff是正确求出的
所以在递归到[n,n][n,n]时,我们在将t(n)t(n)乘上
2.fn=1n1i=1nfigni,gn=dnndfd,f1=12.f_n=\frac{1}{n-1}\sum_{i=1}^{n}f_ig_{n-i},g_{n}=\sum_{d\mid n}^{n}df_d,f_1=1
这种相互递归的式子
我们仍然考虑分治区间[l,r][l,r]
已经计算完[l,mid][l,mid]的所有值,现在的问题是gg[mid+1,r][mid+1,r]上的值还没有计算完
rlmidr3lr-l\le mid\to r\le 3l
条件很难满足
考虑我们能不能分别计算
现在应该是[0,mid][0,mid]已知
我们若按照之前的算法计算
fl,,mid×g1,,midfmid+1,,rf_{l,\dots,mid}\times g_{1,\dots,mid}\to f_{mid+1,\dots,r}
此时少了gg[mid+1,rl][mid+1,r-l]上的贡献
我们考虑分治过程中l,rl,r的关系
只考虑分治到右边,左边同第一个
此时区间为[mid+1,r][mid+1,r],则在之后的任何区间内,都有
r2lr\le 2l也就是rllr-l\le l
所以,只要l1l\neq 1,那么rllr-l\le l
l=1l=1时:
先计算 f1,,mid×g1,,midfmid+1,,rf_{1,\dots,mid}\times g_{1,\dots,mid}\to f_{mid+1,\dots,r}
此时,只有fmidf_{mid}是正确的,
继续递归到下个区间
现在l1l\neq1,那么我们会有f1,,rl,g1,,rlf_{1,\dots,r-l},g_{1,\dots,r-l}都是已知的
先计算完[l,mid][l,mid]区间,
我们已经计算完 f1,l1×g1,l1(1)f_{1,l-1}\times g_{1,l-1}(1)
已知
fi=t(i)j=1i1fjgijf_{i}=t(i)\sum_{j=1}^{i-1}f_{j}g_{i-j}
所以我们需要计算

{fl,mid×g1,rlfmid+1,r(2)f1,rl×gl,midfmid+1,r(3)\begin{cases} f_{l,mid}\times g_{1,r-l}\to f_{mid+1,r}(2)\\ f_{1,r-l}\times g_{l,mid}\to f_{mid+1,r}(3)\\ \end{cases}

我们考虑为什么这是对的
[mid+1,r][mid+1,r]的贡献来自[1,l1],[l,mid][1,l-1],[l,mid]
(1)(1)计算[1,l1][1,l-1]的贡献
(2)(3)(2)(3)计算[1,l1][1,l-1][l,mid][l,mid]的交叉结果
由于rllr-l\le l所以,[l,mid][l,mid]的内部交叉无贡献
发现我们每次这样计算的时候都是计算好了[l,mid][l,mid]对后面区间的贡献
所以这样是正确的

l=1l=1
f1,,mid×g1,,midfmid+1,,rf_{1,,mid}\times g_{1,,mid}\to f_{mid+1,,r}
l1l\neq1

{fl,mid×g1,rlfmid+1,r(2)f1,rl×gl,midfmid+1,r(3)\begin{cases} f_{l,mid}\times g_{1,r-l}\to f_{mid+1,r}(2)\\ f_{1,r-l}\times g_{l,mid}\to f_{mid+1,r}(3)\\ \end{cases}