无标号有根树
设
f
n
f_n
fn为
n
n
n个节点的无标号有根树数量。
把生成函数写出来,设
F
(
x
)
=
∑
i
=
1
∞
f
i
x
i
F(x)=\sum_{i=1}^{\infty}f_ix^i
F(x)=∑i=1∞fixi。
考虑钦点一个点作为根,则需要在下面接若干棵节点数之和为
n
−
1
n-1
n−1的子树。
f
n
=
[
x
n
]
F
(
x
)
=
[
x
n
−
1
]
∏
i
=
1
∞
(
∑
j
=
0
∞
x
i
j
)
f
i
=
[
x
n
−
1
]
∏
i
=
1
∞
(
1
−
x
i
)
−
f
i
=
[
x
n
]
x
∏
i
=
1
∞
(
1
−
x
i
)
−
f
i
\begin{aligned} f_n&=[x^{n}]F(x)\\ &=[x^{n-1}]\prod_{i=1}^{\infty}(\sum_{j=0}^{\infty}x^{ij})^{f_i}\\ &=[x^{n-1}]\prod_{i=1}^{\infty}(1-x^i)^{-f_i}\\ &=[x^{n}]x\prod_{i=1}^{\infty}(1-x^i)^{-f_i} \end{aligned}
fn=[xn]F(x)=[xn−1]i=1∏∞(j=0∑∞xij)fi=[xn−1]i=1∏∞(1−xi)−fi=[xn]xi=1∏∞(1−xi)−fi
上式中等比数列方案数次方的意义就是在每种大小为
i
i
i的不同形态的树中选子树。
F
(
x
)
=
x
∏
i
=
1
∞
(
1
−
x
i
)
−
f
i
\begin{aligned} F(x)=x\prod_{i=1}^{\infty}(1-x^i)^{-f_i} \end{aligned}
F(x)=xi=1∏∞(1−xi)−fi
两边同时取
ln
\ln
ln
ln
F
(
x
)
=
ln
x
−
∑
i
=
1
∞
f
i
ln
(
1
−
x
i
)
\begin{aligned} \ln F(x)=\ln x-\sum_{i=1}^{\infty}f_i\ln(1-x^i) \end{aligned}
lnF(x)=lnx−i=1∑∞filn(1−xi)
两边同时求导
F
′
(
x
)
F
(
x
)
=
1
x
+
∑
i
=
1
∞
f
i
i
x
i
−
1
1
−
x
i
\begin{aligned} \frac{F'(x)}{F(x)}=\frac{1}{x}+\sum_{i=1}^{\infty}f_i\frac{ix^{i-1}}{1-x^i}\\ \end{aligned}
F(x)F′(x)=x1+i=1∑∞fi1−xiixi−1
化简得到
x
F
′
(
x
)
=
F
(
x
)
+
F
(
x
)
∑
i
=
1
∞
i
⋅
f
i
x
i
1
−
x
i
\begin{aligned} xF'(x)=F(x)+F(x)\sum_{i=1}^{\infty}i \cdot f_i\frac{x^{i}}{1-x^i}\\ \end{aligned}
xF′(x)=F(x)+F(x)i=1∑∞i⋅fi1−xixi
单独考虑
x
n
x^n
xn这一项的系数
n
f
n
=
f
n
+
∑
i
=
1
n
−
1
f
i
∑
j
=
1
∞
j
⋅
f
j
⋅
[
x
n
−
i
]
x
j
1
−
x
j
=
f
n
+
∑
i
=
1
n
−
1
f
i
∑
j
=
1
∞
j
⋅
f
j
⋅
[
x
n
−
i
]
∑
k
=
1
∞
x
j
k
=
f
n
+
∑
i
=
1
n
−
1
f
i
∑
j
=
1
∞
j
⋅
f
j
[
j
∣
n
−
i
]
=
f
n
+
∑
i
=
1
n
−
1
f
i
∑
j
∣
n
−
i
j
⋅
f
j
\begin{aligned} nf_n&=f_n+\sum_{i=1}^{n-1}f_i\sum_{j=1}^{\infty}j\cdot f_j\cdot[x^{n-i}]\frac{x^{j}}{1-x^j}\\ &=f_n+\sum_{i=1}^{n-1}f_i\sum_{j=1}^{\infty}j\cdot f_j\cdot[x^{n-i}]\sum_{k=1}^{\infty}x^{jk}\\ &=f_n+\sum_{i=1}^{n-1}f_i\sum_{j=1}^{\infty}j\cdot f_j[j|n-i]\\ &=f_n+\sum_{i=1}^{n-1}f_i\sum_{j|n-i}j\cdot f_j \end{aligned}
nfn=fn+i=1∑n−1fij=1∑∞j⋅fj⋅[xn−i]1−xjxj=fn+i=1∑n−1fij=1∑∞j⋅fj⋅[xn−i]k=1∑∞xjk=fn+i=1∑n−1fij=1∑∞j⋅fj[j∣n−i]=fn+i=1∑n−1fij∣n−i∑j⋅fj
化简得
f
n
=
∑
i
=
1
n
−
1
f
i
∑
j
∣
n
−
i
j
⋅
f
j
n
−
1
f_n=\frac{\sum_{i=1}^{n-1}f_i\sum_{j|n-i}j\cdot f_j}{n-1}
fn=n−1∑i=1n−1fi∑j∣n−ij⋅fj
可以
n
2
n^2
n2递推,也可以
n
log
2
2
n
n\log_2^2n
nlog22n分治
F
F
T
FFT
FFT。
分治的时候把贡献拆成两部分算,而且取的是多项式后一半,前一半循环卷积了也不用管。
无标号无根树
设
g
n
g_n
gn为
n
n
n个节点的无标号无根树数量。
只考虑以重心为根的有根树,进行容斥。
当
n
n
n为奇数时,
g
n
=
f
n
−
∑
i
=
1
⌊
n
2
⌋
f
i
f
n
−
i
g_n=f_n-\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}f_if_{n-i}
gn=fn−i=1∑⌊2n⌋fifn−i
若
n
n
n为偶数,则一棵树有两个重心,同一棵树有两种方法拼出来,需要特殊处理一下。
当
n
n
n为偶数时,
g
n
=
f
n
−
(
∑
i
=
1
n
2
−
1
f
i
f
n
−
i
)
−
f
n
2
(
f
n
2
−
1
)
2
=
f
n
−
(
∑
i
=
1
n
2
f
i
f
n
−
i
)
+
f
n
2
(
f
n
2
+
1
)
2
\begin{aligned} g_n&=f_n-(\sum_{i=1}^{\frac{n}{2}-1}f_if_{n-i})-\frac{f_{\frac{n}{2}}(f_{\frac{n}{2}}-1)}{2}\\ &=f_n-(\sum_{i=1}^{\frac{n}{2}}f_if_{n-i})+\frac{f_{\frac{n}{2}}(f_{\frac{n}{2}}+1)}{2} \end{aligned}
gn=fn−(i=1∑2n−1fifn−i)−2f2n(f2n−1)=fn−(i=1∑2nfifn−i)+2f2n(f2n+1)
简单代码
for(int i=1;i<=n;i++){
if(i==1){
f[i]=1;
}else{
for(int j=1;j<i;j++){
f[i]=(f[i]+1LL*f[j]*g[i-j])%mod;
}
f[i]=1LL*f[i]*fastpow(i-1,mod-2)%mod;
}
int tmp=1LL*i*f[i]%mod;
for(int j=i;j<=n;j+=i){
g[j]=(g[j]+tmp)%mod;
}
}
待填坑:时间复杂度为 n l o g 2 n nlog_2n nlog2n的多项式 ln , exp \ln,\exp ln,exp牛顿迭代做法。
n l o g 2 2 n nlog_2^2n nlog22n分治做法
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=530005,mod=998244353;
int n,len,res,rev[N],f[N],g[N],a[N],b[N];
inline int add(int a,int b){
a+=b;
return a<mod?a:a-mod;
}
inline int dec(int a,int b){
a-=b;
return a<0?a+mod:a;
}
inline int mul(int a,int b){
return 1LL*a*b%mod;
}
int fastpow(int a,int x){
int res=1;
while(x){
if(x&1){
res=mul(res,a);
}
x>>=1;
a=mul(a,a);
}
return res;
}
void ntt(int a[],int len,int dft){
for(int i=0;i<len;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)*(len>>1));
if(i<rev[i]){
swap(a[i],a[rev[i]]);
}
}
for(int i=1;i<len;i<<=1){
int wn=fastpow(3,(mod-1)/i/2);
if(dft==-1){
wn=fastpow(wn,mod-2);
}
for(int j=0;j<len;j+=(i<<1)){
int w=1,x,y;
for(int k=j;k<j+i;k++,w=mul(w,wn)){
x=a[k];
y=mul(w,a[k+i]);
a[k]=add(x,y);
a[k+i]=dec(x,y);
}
}
}
if(dft==-1){
int inv=fastpow(len,mod-2);
for(int i=0;i<len;i++){
a[i]=mul(a[i],inv);
}
}
}
void solve(int l,int r,int len){
if(l==r){
f[l]+=(l==1);
if(l>1){
f[l]=mul(f[l],fastpow(l-1,mod-2));
}
int t=mul(f[l],l);
for(int i=l;i<=n&&l;i+=l){
g[i]=add(g[i],t);
}
return;
}
solve(l,l+(len>>1)-1,len>>1);
for(int i=0;i<len;i++){
a[i]=(i<(len>>1))?f[l+i]:0;
b[i]=(i<l+(len>>1))?g[i]:0;
}
ntt(a,len,1);
ntt(b,len,1);
for(int i=0;i<len;i++){
a[i]=mul(a[i],b[i]);
}
ntt(a,len,-1);
for(int i=(len>>1);i<len;i++){
f[l+i]=add(f[l+i],a[i]);
}
for(int i=0;i<len;i++){
a[i]=(i<(len>>1))?g[l+i]:0;
b[i]=(i<l)?f[i]:0;
}
ntt(a,len,1);
ntt(b,len,1);
for(int i=0;i<len;i++){
a[i]=mul(a[i],b[i]);
}
ntt(a,len,-1);
for(int i=(len>>1);i<len;i++){
f[l+i]=add(f[l+i],a[i]);
}
solve(l+(len>>1),r,len>>1);
}
int main(){
scanf("%d",&n);
for(len=1;len<=n;len<<=1);
solve(0,len-1,len);
res=f[n];
for(int i=1;(i<<1)<=n;i++){
res=dec(res,mul(f[i],f[n-i]));
}
if(~n&1){
res=add(res,1LL*f[n>>1]*(f[n>>1]+1)/2%mod);
}
printf("%d\n",res);
return 0;
}