P5900 无标号无根树计数 题解

不懂为啥都要对原式神秘转化之后再牛顿迭代,直接对原式牛顿迭代即可!完全不用转化!

设无标号有根树的组合类是 T,则有 T=Z×MSET(T),即 T(x)=xexpj1T(xj)j

G(F(x))=F(x)xexpj1F(xj)j=0,要求 F(x)modxn,问题变为牛顿迭代的形式。

首先钦定 F(x)modx1=[x0]F(x)=0(否则 expj1F(xj)j 的常数项不收敛),

然后假设已经求出模 xn2 意义下的解 F0(x),则模 xn 意义下的解 F(x)F0(x)G(F0(x))G(F0(x))(modxn)

考虑如何求 G(F0(x))

观察到 G(F0(x))=F0(x)xexpF0(x)expj2F0(xj)j

设最终答案是 H(x)(这里 H(x) 是与 F0(x) 无关的常量),

j2,有 F0(xj)H(xj)(modxn),则 expj2F0(xj)jexpj2H(xj)j(modxn)

于是 expj2F0(xj)j 是与 F0(x) 无关的常量,

G(F0(x))=1xexpF0(x)expj2F0(xj)j=1xexpj1F0(xj)j

于是 F(x)F0(x)G(F0(x))G(F0(x))F0(x)F0(x)xexpj1F0(xj)j1xexpj1F0(xj)j(modxn)

现在我们会算无标号有根树了,设 fi 表示 i 个点的无标号有根树个数,考虑怎么算无标号无根树。

n 为奇数时,每个无标号无根树都有唯一的重心,所以只需统计根为重心的无标号有根树个数,

考虑用 fn 减去根不为重心的无标号有根树个数,这些树一定有一棵大小超过 n2 的子树,

枚举这棵子树的大小,则答案为 fni=n2+1n1fi×fni

n 为偶数时,考虑用同样的计数方法,

发现有两个重心的树,即有一棵大小为 n2 的子树的树,可能会被重复统计,

设这棵大小为 n2 的子树为 T,若原树去掉 T 后剩下的树与 T 完全一致,

则原树分别以两个重心为根形成的有根树完全一致,这样的树不会被重复统计,

反之若原树去掉 T 后剩下的树与 T 不同,这样的树就会被重复统计,

所以从大小为 n2 的有根树中任选出两棵,就可以组合出一种被重复统计的树,

所以有 (fn22) 种被重复统计的树,答案减去 (fn22) 即可。

没有封装多项式类,所以代码比较混乱邪恶,建议谨慎阅读。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define G 3
#define _G 332748118
#define M 998244353
using namespace std;
int n, l, r[6000050], f[6000050], g[6000050], h[6000050], x[6000050], y[6000050], z[6000050], k[6000050], v[6000050];
int P(int x, int y)
{
    int q = 1;
    for (; y; y >>= 1, x = x * x % M)
        if (y & 1)
            q = q * x % M;
    return q;
}
void F(int *f, int n, int v)
{
    for (int i = 0; i < n; ++i)
        if (i < r[i])
            swap(f[i], f[r[i]]);
    for (int L = 2, m; L <= n; L <<= 1)
    {
        m = L >> 1;
        int W = P(v == 1 ? G : _G, (M - 1) / L);
        for (int l = 0, r = L - 1; r <= n; l += L, r += L)
        {
            int o = 1;
            for (int p = l; p < l + m; ++p)
            {
                int x = f[p], y = f[p + m];
                f[p] = (x + o * y) % M, f[p + m] = (x + M - o * y % M) % M;
                o = o * W % M;
            }
        }
    }
}
void I(int *f, int *g, int n)
{
    memset(g, 0, n << 4);
    memset(x, 0, n << 4);
    g[0] = P(f[0], M - 2);
    int L;
    for (L = 4;; L <<= 1)
    {
        memcpy(x, f, L << 2);
        memcpy(y, g, L << 3);
        l = __lg(L);
        for (int i = 0; i < L; ++i)
            r[i] = r[i >> 1] >> 1 | (i & 1) << l - 1;
        F(x, L, 1);
        F(y, L, 1);
        for (int i = 0; i < L; ++i)
            x[i] = x[i] * y[i] % M;
        F(x, L, -1);
        int _ = P(L, M - 2);
        for (int i = 0; i < L; ++i)
            x[i] = (M - x[i] * _ % M) % M;
        x[0] = (x[0] + 2) % M;
        memset(x + (L >> 1), 0, L << 2);
        F(g, L, 1);
        F(x, L, 1);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * x[i] % M;
        F(g, L, -1);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * _ % M;
        if (L >> 1 >= n)
            break;
    }
    memset(g + n, 0, L - n << 3);
}
void LN(int *f, int *g, int n)
{
    memset(h, 0, n << 4);
    for (int i = 0; i < n - 1; ++i)
        h[i] = (i + 1) * f[i + 1] % M;
    I(f, g, n);
    int L = 1;
    while (L >> 1 < n)
        L <<= 1;
    l = __lg(L);
    for (int i = 0; i < L; ++i)
        r[i] = r[i >> 1] >> 1 | (i & 1) << l - 1;
    F(g, L, 1);
    F(h, L, 1);
    for (int i = 0; i < L; ++i)
        h[i] = g[i] * h[i] % M;
    F(h, L, -1);
    int _ = P(L, M - 2);
    for (int i = 0; i < L; ++i)
        h[i] = h[i] * _ % M;
    g[0] = 0;
    for (int i = 1; i < n; ++i)
        g[i] = h[i - 1] * P(i, M - 2) % M;
    memset(g + n, 0, L - n << 3);
}
void EXP(int *f, int *g, int n)
{
    memset(g, 0, n << 4);
    g[0] = 1;
    int L;
    for (L = 4;; L <<= 1)
    {
        LN(g, z, L >> 1);
        for (int i = 0; i < L >> 1; ++i)
            z[i] = (f[i] + M - z[i]) % M;
        z[0] = (z[0] + 1) % M;
        l = __lg(L);
        for (int i = 0; i < L; ++i)
            r[i] = r[i >> 1] >> 1 | (i & 1) << l - 1;
        F(g, L, 1);
        F(z, L, 1);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * z[i] % M;
        F(g, L, -1);
        int _ = P(L, M - 2);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * _ % M;
        memset(g + (L >> 1), 0, L << 2);
        if (L >> 1 >= n)
            break;
    }
    memset(g + n, 0, L - n << 3);
}
signed main()
{
    v[1] = 1;
    for (int i = 2; i <= 6e6; ++i)
        v[i] = (M - M / i) * v[M % i] % M;
    scanf("%lld", &n), ++n;
    for (int L = 4;; L <<= 1)
    {
        memset(g, 0, L << 2);
        for (int i = 1; i < L >> 2; ++i)
            for (int j = i; j < L >> 1; j += i)
                g[j] = (g[j] + f[i] * i % M * v[j]) % M;
        EXP(g, k, L >> 1);
        for (int i = (L >> 1) - 1; i >= 1; --i)
            k[i] = (M - k[i - 1]) % M;
        k[0] = 1;
        I(k, g, L >> 1);
        k[0] = 0;
        for (int i = 1; i < L >> 2; ++i)
            k[i] = (k[i] + f[i]) % M;
        F(g, L, 1);
        F(k, L, 1);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * k[i] % M;
        F(g, L, -1);
        int _ = P(L, M - 2);
        for (int i = 0; i < L; ++i)
            g[i] = g[i] * _ % M;
        for (int i = 0; i < L >> 1; ++i)
            f[i] = (f[i] + M - g[i]) % M;
        if (L >> 1 >= n)
            break;
    }
    --n;
    int q = f[n];
    for (int i = (n >> 1) + 1; i < n; ++i)
        q = (q + M - f[i] * f[n - i] % M) % M;
    if (!(n & 1))
    {
        int u = f[n >> 1];
        q = (q + M - u * (u - 1) % M * (M + 1 >> 1) % M) % M;
    }
    printf("%lld", q);
    return 0;
}
posted @   Jijidawang  阅读(49)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示