前置skill
基础求导
(全世界应该只有我不会求导了吧……=。=,但考虑到可能有小学生在看这篇文章,所以还是讲一下吧
导数,是微积分中的重要基础概念,关于它的具体内容就不讲了,在本文中,你只要知道,求导是用来拟合一个函数在各处的斜率的东西,并且有:
若
f(x)=xa
f
(
x
)
=
x
a
,则
f′(x)=axa−1
f
′
(
x
)
=
a
x
a
−
1
(
f′(x)
f
′
(
x
)
:一阶导数)。由此可知,还有
f″(x)=a(a−1)xa−2
f
″
(
x
)
=
a
(
a
−
1
)
x
a
−
2
(
f″(x)
f
″
(
x
)
:二阶导数)等等。
泰勒展开
将一个函数在
x0
x
0
处泰勒展开可得:
多项式求逆
多项式牛顿迭代
已知 G(x) G ( x ) ,求解 G(F(x))≡0(modxn) G ( F ( x ) ) ≡ 0 ( mod x n )
假设已经存在
G(H(x))≡0(modxn2)
G
(
H
(
x
)
)
≡
0
(
mod
x
n
2
)
我们把
G(F(x))
G
(
F
(
x
)
)
在
H(x)
H
(
x
)
处泰勒展开一下。
又因为
G(H(x))≡0(modxn2)
G
(
H
(
x
)
)
≡
0
(
mod
x
n
2
)
,而
G(F(x))≡0(modxn)
G
(
F
(
x
)
)
≡
0
(
mod
x
n
)
,
G(F(x))
G
(
F
(
x
)
)
中
F(x)
F
(
x
)
次数较小的
n2
n
2
项在模
xn
x
n
意义下能够产生的贡献是0,而
G
G
是一个多项式,所以次数较大的项不会影响次数较小的这些项,因此
F(x)
F
(
x
)
的前
n2
n
2
项就是
H(x)
H
(
x
)
,则
F(x)−H(x)
F
(
x
)
−
H
(
x
)
的前
n2
n
2
项为0,则
(F(x)−H(x))2(modxn)≡0
(
F
(
x
)
−
H
(
x
)
)
2
(
mod
x
n
)
≡
0
。
则有:
也就是:
于是我们可以不断缩小 n n 的规模,同时利用多项式求逆来搞这些东西了。
多项式开根
即求,
P(x)
P
(
x
)
已知,就是一个常数。
那么我们设
G(A)=A2−P
G
(
A
)
=
A
2
−
P
,于是我们就可以利用多项式牛顿迭代的式子来求解
F(x)
F
(
x
)
辣。
很容易知道:
例题
codeforces 438E/bzoj3625 小朋友和二叉树
我们设
f(x)
f
(
x
)
表示权值和为
x
x
的二叉树形态个数,设表示
c
c
集合中是否存在权值,则通过枚举根节点权值和根节点两个叉上子树的权值和,可得一个递推式:
边界: f(0)=1 f ( 0 ) = 1
也就是说, f(x)=f(x)2a(x)+1 f ( x ) = f ( x ) 2 a ( x ) + 1
解得: f(x)=1±1−4a(x)√2a(x) f ( x ) = 1 ± 1 − 4 a ( x ) 2 a ( x )
分子分母同时乘以 1∓1−4a(x)‾‾‾‾‾‾‾‾‾√ 1 ∓ 1 − 4 a ( x ) ,并舍去分母有可能为0的那个解可得:
多项式求逆+开根即可完成本题。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int mod=998244353,N=262150,G=3;
int n,m,inv2;
int A[N],B[N],C[N],rev[N],c[N],tmp[N],len[N];
int ksm(int x,int y) {
int re=1;
for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
return re;
}
void NTT(int *a,int n,int x) {
for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
for(RI i=1;i<n;i<<=1) {
int gn=ksm(G,(mod-1)/(i<<1));
for(RI j=0;j<n;j+=(i<<1)) {
int t1,t2,g=1;
for(RI k=0;k<i;++k,g=1LL*g*gn%mod) {
t1=a[j+k],t2=1LL*g*a[j+i+k]%mod;
a[j+k]=(t1+t2)%mod,a[j+i+k]=(t1-t2+mod)%mod;
}
}
}
if(x==1) return;
reverse(a+1,a+n);int inv=ksm(n,mod-2);
for(RI i=0;i<n;++i) a[i]=1LL*a[i]*inv%mod;
}
void getrev(int t)
{for(RI i=0;i<t;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len[t]-1));}
void getinv(int *a,int *b,int n) {
if(n==1) {b[0]=ksm(a[0],mod-2);return;}
getinv(a,b,n>>1);
int kn=n<<1;getrev(kn);
for(RI i=0;i<n;++i) c[i]=a[i],c[n+i]=0;
NTT(c,kn,1),NTT(b,kn,1);
for(RI i=0;i<kn;++i) b[i]=1LL*b[i]*(2-1LL*c[i]*b[i]%mod+mod)%mod;
NTT(b,kn,-1);
for(RI i=n;i<kn;++i) b[i]=0;
}
void getsqrt(int *a,int *b,int n) {
if(n==1) {b[0]=1;return;}
getsqrt(a,b,n>>1);int kn=n<<1;
for(RI i=0;i<n;++i) tmp[i]=0;
getinv(b,tmp,n),getrev(kn);
for(RI i=0;i<n;++i) c[i]=a[i],c[n+i]=0;
NTT(c,kn,1),NTT(b,kn,1),NTT(tmp,kn,1);
for(RI i=0;i<kn;++i) b[i]=1LL*(b[i]+1LL*c[i]*tmp[i]%mod)%mod*inv2%mod;
NTT(b,kn,-1);
for(RI i=n;i<kn;++i) b[i]=0;
}
int main()
{
n=read(),m=read();
for(RI i=1;i<=n;++i) A[read()]=1;
int kn=1;while(kn<=m) kn<<=1,len[kn]=len[kn>>1]+1;
len[kn<<1]=len[kn]+1;
for(RI i=0;i<kn;++i) A[i]=(mod-4LL*A[i]%mod)%mod;
A[0]=1,inv2=ksm(2,mod-2);
getsqrt(A,B,kn),B[0]=(B[0]+1)%mod;
getinv(B,C,kn);
for(RI i=1;i<=m;++i) printf("%d\n",(C[i]+C[i])%mod);
return 0;
}