引入
-
定义两棵无标号有根树相同,当且仅当存在一个给这两棵树点编号的方案(根的编号必须相同),使得编号后的两棵树相同
-
定义两棵无标号无根树相同,当且仅当存在一个给这两棵树点编号的方案,使得编号后的两棵树相同
-
这里给出无标号有根树和无根树计数的 (O(nlog^2n)) 做法
无标号有根树计数
-
设 (f_n) 为 (n) 个点无标号有根树的个数
-
不难发现两棵有根树同构,当且仅当存在一个 ((i,j)),满足根的大小为 (i) 且形态为 (j)(我们把大小为 (i) 的有根树的形态依次编号为 (1) 到 (f_i))的子树个数不同
-
设 (f) 的 OGF 为 (F(x)),我们有以下式子:
-
[F(x)=xprod_{i>0}(frac1{1-x^i})^{f_i} ]
-
式子右边第一个 (x) 表示根,(i) 表示子树大小,(frac1{1-x^i}) 表示对于一种大小为 (i) 的指定形态的子树,这种子树占用 (i imes j)((jge 0))个点的方案数为均 (1),((frac1{1-x^i})^{f_i}) 自然就表示大小为 (i) 的所有形态的子树
-
这种多个多项式乘积的式子我们通常采用取对数的方法:
-
[ln F(x)=ln x-sum_{i>0}f_iln(1-x^i) ]
-
再求导:
-
[frac{F'(x)}{F(x)}=frac 1x+sum_{i>0}if_ifrac{x^{i-1}}{1-x^i} ]
-
[xF'(x)=F(x)+F(x)sum_{i>0}if_ifrac{x^i}{1-x^i} ]
-
比较式子两边的系数:
-
[nf_n=f_n+sum_{i>0}if_isum_{j>0,i|j}f_{n-j} ]
-
设 (g_i=sum_{j|i}jf_j),那么我们有:
-
[(n-1)f_n=sum_{i>0}f_ig_{n-i} ]
-
(f) 用分治 FFT 求,每求出一个 (f) 之后都枚举倍数更新 (g),即可做到 (O(nlog^2n)) 的复杂度
无标号无根树计数
-
由于一棵树的重心只有 (1) 或 (2) 个,所以我们考虑把根定义为重心
-
考虑容斥,用 (f_n) 减去根不是重心的方案数:
-
[h_n=sum_{i=1}^{lfloorfrac n2 floor}f_if_{n-i} ]
-
注意特殊情况:当 (n) 是偶数时重心可以有两个,当 (i=frac n2) 时,我们可以强制把重心边删掉之后,根所在的子树形态编号不小于另一个,故减去 (inom{f_i}2) 即可
-
如果需要预处理所有的 (h),由于 (ilefrac n2) 相当于 (ile n-i),而 (i<n-i) 和 (i>n-i) 是对称的,所以把多项式 (F) 平方之后每项系数除以 (2),再特殊处理一下偶数的情况即可:
-
[h_n=f_n-frac 12([x_n]F(x)^2-f_{frac n2}[nmod 2=0]) ]
-
这一部分的处理复杂度是 (O(nlog n)) 的,但求 (f) 的复杂度还是 (O(nlog^2n))
Code(无标号无根树)
#include <bits/stdc++.h>
template <class T>
inline void read(T &res)
{
res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
if (bo) res = ~res + 1;
}
template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}
const int N = 2e5 + 5, M = 8e5 + 5, djq = 998244353, I2 = 499122177;
int f[N], g[N], h[N], inv[N], rev[M], yg[M], A[M], B[M], ff, tot;
inline void add(int &a, const int &b) {if ((a += b) >= djq) a -= djq;}
inline void sub(int &a, const int &b) {if ((a -= b) < 0) a += djq;}
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = 1ll * res * a % djq;
a = 1ll * a * a % djq;
b >>= 1;
}
return res;
}
void FFT(int n, int *a, int op)
{
for (int i = 0; i < n; i++) if (i < rev[i]) std::swap(a[i], a[rev[i]]);
yg[n] = qpow(3, (djq - 1) / n * ((n + op) % n));
for (int i = n >> 1; i; i >>= 1)
yg[i] = 1ll * yg[i << 1] * yg[i << 1] % djq;
for (int k = 1; k < n; k <<= 1)
{
int x = yg[k << 1];
for (int i = 0; i < n; i += k << 1)
{
int w = 1;
for (int j = 0; j < k; j++)
{
int u = a[i + j], v = 1ll * w * a[i + j + k] % djq;
add(a[i + j] = u, v); sub(a[i + j + k] = u, v);
w = 1ll * w * x % djq;
}
}
}
if (op == -1)
{
int gg = qpow(n, djq - 2);
for (int i = 0; i < n; i++) a[i] = 1ll * a[i] * gg % djq;
}
}
void polymul(int n, int m)
{
ff = 1; tot = 0;
while (ff <= n + m) ff <<= 1, tot++;
for (int i = 0; i < ff; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
for (int i = n + 1; i < ff; i++) A[i] = 0;
for (int i = m + 1; i < ff; i++) B[i] = 0;
FFT(ff, A, 1); FFT(ff, B, 1);
for (int i = 0; i < ff; i++) A[i] = 1ll * A[i] * B[i] % djq;
FFT(ff, A, -1);
}
void czx(int l, int r)
{
if (l == r)
{
if (l == 1) f[r] = 1; else f[r] = 1ll * f[r] * inv[l - 1] % djq;
for (int i = l; i <= 200000; i += l)
g[i] = (1ll * l * f[r] + g[i]) % djq;
return;
}
int mid = l + r >> 1, l1 = mid - l, l2 = Min(r - l, mid);
czx(l, mid);
for (int i = 0; i <= l1; i++) A[i] = f[l + i];
for (int i = 0; i <= l2; i++) B[i] = g[i];
polymul(l1, l2);
for (int i = mid + 1; i <= r; i++) if (i - l < ff) f[i] = (f[i] + A[i - l]) % djq;
l2 = Min(r - l, l - 1);
for (int i = 0; i <= l1; i++) A[i] = g[l + i];
for (int i = 0; i <= l2; i++) B[i] = f[i];
polymul(l1, l2);
for (int i = mid + 1; i <= r; i++) if (i - l < ff) f[i] = (f[i] + A[i - l]) % djq;
czx(mid + 1, r);
}
int main()
{
int T, n;
inv[1] = 1;
for (int i = 2; i <= 200000; i++)
inv[i] = 1ll * (djq - djq / i) * inv[djq % i] % djq;
czx(1, 200000); read(T);
for (int i = 0; i <= 200000; i++) A[i] = B[i] = h[i] = f[i];
polymul(200000, 200000);
for (int i = 1; i <= 200000; i++)
h[i] = (f[i] - 1ll * I2 * (A[i] - (i & 1 ? 0 : f[i >> 1])
+ djq) % djq + djq) % djq;
while (T--) read(n), printf("%d
", h[n]);
return 0;
}