【题解】CF438E The Child and Binary Tree
数学不好,大概率是不严谨的。
思路
生成函数 + 卷积。
首先意识到值域是 \(10^5\),似乎可以搞事情。并且此题还是对 \(998244353\) 取模的数树题,可以考虑生成函数。
首先令 \(f_n\) 表示合法且权值为 \(n\) 的二叉树个数。
考虑钦定树根的值,可以枚举左右子树的权值和转移:
\(f_n = \sum\limits_{i = 1}^n [i \in c] \sum\limits_{j = 1}^{n - i} f_j \cdot f_{n - i - j}\).
边界条件是 \(f_0 = 1\),即空树。
对于 \([i \in c]\),可以考虑令它等于 \(g_i\).
令 \(F\) 为 \(f\) 的生成函数,\(G\) 为 \(g\) 的生成函数。
考虑上式的形式,发现实际上是三个多项式的卷积,也就是 \(G * F^2\).
所以原式等价于 \(F = G * F^2 + 1\).
一元二次方程求根公式得 \(F = \frac{1 \pm \sqrt{1 - 4G}}{2G}\).
然而注意到权值都是整数,所以 \(G\) 的零次项为 \(0\),导致 \(G\) 不能做多项式开根。
所以考虑分子有理化成 \(\frac{2}{1 \pm \sqrt{1 - 4G}}\).
注意到 \(\lim\limits_{x \rightarrow 0} \sqrt{1 - 4G} = \sqrt{1 - 4 \cdot [x^0] G(x)} = 1\).
所以取正时 \(\lim\limits_{x \rightarrow 0} \frac{2}{1 \pm \sqrt{1 - 4G}} = 1\),反之分母会出问题。
所以取正,然后多项式求逆 + 开根就行。
这里有神仙指出在形式幂级数意义下 \(G\) 不可求逆,不太懂,有懂哥可以教一下吗 /kk
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int sz = 3e5 + 5;
const int mod = 998244353;
const int g = 3;
int n, m;
int c[sz], rev[sz];
ll G[sz], inv[sz], tmp[sz], sq[sz], wp[sz];
ll Ft[sz], Rt[sz], ft[sz], rt[sz];
void calc_rev(int k) { for (int i = 1; i < k; i++) rev[i] = (rev[i >> 1] >> 1 | (i & 1 ? k >> 1 : 0)); }
ll qpow(ll base, ll power, ll mod)
{
ll res = 1;
while (power)
{
if (power & 1) res = res * base % mod;
base = base * base % mod;
power >>= 1;
}
return res;
}
void NTT(ll *A, int n)
{
calc_rev(n);
for (int i = 1; i < n; i++)
if (rev[i] > i) swap(A[i], A[rev[i]]);
for (int len = 2, m = 1; len <= n; m = len, len <<= 1)
{
ll wn = qpow(g, (mod - 1) / len, mod);
wp[0] = 1;
for (int i = 1; i <= len; i++) wp[i] = wp[i - 1] * wn % mod;
for (int l = 0, r = len - 1; r <= n; l += len, r += len)
{
int w = 0;
for (int p = l; p < l + m; p++, w++)
{
ll x = A[p], y = wp[w] * A[p + m] % mod;
A[p] = (x + y) % mod, A[p + m] = (x - y + mod) % mod;
}
}
}
}
void INTT(ll *A, int n)
{
NTT(A, n);
reverse(A + 1, A + n);
int inv = qpow(n, mod - 2, mod);
for (int i = 0; i < n; i++) A[i] = 1ll * A[i] * inv % mod;
}
void invp(ll *f, ll *r, int n)
{
int k = 1;
while (k < n) k <<= 1;
r[0] = qpow(f[0], mod - 2, mod);
for (int len = 2, m = 1; len <= k; m = len, len <<= 1)
{
for (int i = 0; i < len; i++) Rt[i] = r[i], Ft[i] = f[i];
NTT(Ft, len), NTT(Rt, len);
for (int i = 0; i < len; i++) Rt[i] = Rt[i] * Ft[i] % mod;
INTT(Rt, len);
for (int i = 0; i < m; i++) Rt[i] = 0; Rt[0] = 1;
for (int i = 0; i < len; i++) Ft[i] = r[i];
NTT(Ft, len), NTT(Rt, len);
for (int i = 0; i < len; i++) Rt[i] = Rt[i] * Ft[i] % mod;
INTT(Rt, len);
for (int i = m; i < len; i++) r[i] = (r[i] * 2ll - Rt[i] + mod) % mod;
}
memset(Ft, 0, k * sizeof(ll));
memset(Rt, 0, k * sizeof(ll));
for (int i = n; i < k; i++) r[i] = 0;
}
void sqrtp(ll *f, ll *s, int n)
{
s[0] = 1;
int k = 1;
while (k < (n << 1)) k <<= 1;
ll inv2 = qpow(2, mod - 2, mod);
for (int len = 2, m = 1; len <= k; m = len, len <<= 1)
{
invp(s, rt, m);
for (int i = 0; i < m; i++) ft[i] = f[i];
NTT(ft, len), NTT(rt, len);
for (int i = 0; i < len; i++) rt[i] = rt[i] * ft[i] % mod;
INTT(rt, len);
for (int i = 0; i < m; i++) rt[i] = (rt[i] + s[i]) % mod;
for (int i = 0; i < len; i++) s[i] = rt[i] * inv2 % mod, rt[i] = ft[i] = 0;
}
for (int i = 0; i < k; i++) rt[i] = ft[i] = 0;
for (int i = n; i < k; i++) s[i] = 0;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &c[i]), G[c[i]] = 1;
tmp[0] = 1; for (int i = 1; i <= m; i++) tmp[i] = mod - 4 * G[i] % mod;
sqrtp(tmp, sq, m + 1);
sq[0]++;
invp(sq, inv, m + 1);
for (int i = 1; i <= m; i++) inv[i] = 2ll * inv[i] % mod;
for (int i = 1; i <= m; i++) printf("%lld\n", inv[i]);
return 0;
}