Online (relaxed) convolution returns as soon as and are known, so and may depend on : the tool for sequences defined by a convolution with their own prefix (, rooted-tree counts, / of something defined implicitly). Cost ( in about 0.6 s), versus naively. Uses ntt/convolution/Poly from NTT.

SettingMethodCost
, known in advance (semi-relaxed)CDQ divide and conquer: finish the left half, add its contribution to the right half with one convolution, recurse
and both depend on the prefix of (fully relaxed)RelaxedConvolution (blocks of size along the anti-diagonals)
the equation is algebraic in a known series (, inverse, log of a known )Newton (Power Series)
Poly cdqSelfConvolution(const Poly& g, mint f0, int n) {    // f_0 = f0, f_i = sum_{j<i} f_j g_{i-j}  (i.e. f = f0 / (1 - g) with g_0 ignored)
    Poly f(n, 0); if (n == 0) return f; f[0] = f0;
    auto rec = [&](auto&& self, int l, int r) -> void {      // on entry f[l] is final; contributions from [0, l) already added to f[l, r)
        if (r - l == 1) return;
        int m = (l + r) / 2; self(self, l, m);
        Poly a(f.begin() + l, f.begin() + m), b(min(r - l, sz(g)), 0);
        for (int i = 1; i < sz(b); i++) b[i] = g[i];
        Poly c = polyMul(a, b);
        for (int i = m; i < r; i++) if (i - l < sz(c)) f[i] += c[i - l];
        self(self, m, r);
    };
    rec(rec, 0, n);
    return f;
}
 
template <int P> struct RelaxedConvolution {
    using Z = Mint<P>; static constexpr int NAIVE = 32;
    vector<Z> f, g, h; vector<vector<Z>> fBand, gBand;     // NTT (size 2s) of f[s-1, 2s-1) and g[s-1, 2s-1), s = 2^z
    Z push(Z a, Z b) {                                       // h_p = sum_{j + k = p} f_j g_k, with f_p = a, g_p = b
        int p = sz(f); f.push_back(a); g.push_back(b);
        if (sz(h) < 2 * p + 2) h.resize(2 * p + 2);
        for (int z = 0, s = 1; 2 * s <= p + 2 && (p + 2) % s == 0; z++, s *= 2) {
            int x = p + 1 - s; bool diag = x == s - 1;       // the square [s-1, 2s-1)^2, counted once
            if (s <= NAIVE) {
                for (int j = x; j < x + s; j++) for (int k = s - 1; k < 2 * s - 1; k++) h[j + k] += diag ? f[j] * g[k] : f[j] * g[k] + g[j] * f[k];
                continue;
            }
            vector<Z> u(f.begin() + x, f.begin() + x + s), v(g.begin() + x, g.begin() + x + s);
            u.resize(2 * s); v.resize(2 * s); ntt(u); ntt(v);
            if (diag) { fBand.resize(z + 1); gBand.resize(z + 1); fBand[z] = u; gBand[z] = v; for (int i = 0; i < 2 * s; i++) u[i] *= v[i]; }
            else for (int i = 0; i < 2 * s; i++) u[i] = u[i] * gBand[z][i] + v[i] * fBand[z][i];
            ntt(u, true);
            for (int i = 0; i < 2 * s - 1; i++) h[p + i] += u[i];
        }
        return h[p];
    }
};
 
vector<mint> rootedTrees(int n) {                           // unlabelled rooted trees a_1..a_n (index = size), relaxed
    vector<mint> a(n + 1, 0), b(n + 1, 0), iv(n + 1, 1); if (n >= 1) a[1] = 1;
    for (int i = 2; i <= n; i++) iv[i] = -mint(998244353 / i) * iv[998244353 % i];
    RelaxedConvolution<998244353> rc;                       // h_m = sum_{k=1}^{m} b_k a_{m+1-k}: push (b_{i+1}, a_{i+1})
    for (int m = 1; m < n; m++) {
        for (int d = 1; d * d <= m; d++) if (m % d == 0) { b[m] += mint(d) * a[d]; if (d * d != m) b[m] += mint(m / d) * a[m / d]; }
        mint h = rc.push(b[m], a[m]);                        // after push number m - 1 (0-indexed): sum_{j+k = m-1} b_{j+1} a_{k+1}
        a[m + 1] = h * iv[m];
    }
    return a;
}

Rooted trees. gives with (Graph Counting): needs , and needs the convolution up to , a relaxed product of the shifted sequences and .