P5333 [JSOI2019]神经网络

P5333 [JSOI2019]神经网络

Solution

EGF 表示有标号排列。

对每棵树分别算出划分成 \(i\) 条链的方案数,记为 \(f_i\)

具体地:设 \(dp[u][i][0/1/2]\) 表示在 \(u\) 子树内拆分成 \(i\) 条已结束的链,

\(0\): 已拼完,无法再延伸

\(1\): 单点,可继续向上扩展

\(2\): 长度 \(>1\) 的链,且可继续向上扩展 (为了处理正反向方案数 \(\times 2\) 的问题)

对于根节点视作它可以向父节点扩展,即和其它节点同等处理

状态由划分的客观形态决定 不能对 \(1/2\) 情况人为钦定为 \(0\)

\(f_i = dp[root][i][0] + dp[root][i - 1][1] + 2 \times dp[root][i - 1][2]\)

\(dp\) 转移有点繁复,但写出来极度舒适

把所有链拼成一个路径排列,则相邻的链颜色不同。

对于其他树,EGF 为:(采用容斥思想,枚举相邻位置个数)

\[\sum\limits_{i = 1}^{k}f_i\times i! \times \sum\limits_{j = 0}^{i - 1}(-1)^{j}\binom{i - 1}{j}\frac{x^{i - j}}{(i - j)!} \]

对于第一棵树,第一条链被固定为起点,且第一棵树最后一条链不能与第一条链相邻。

第一条链被固定为起点:但统计一号点不必作为排列的起点,只需平移即可得到合法哈密顿回路。

第一棵树最后一条链不能与第一条链相邻:可以把两条链拼起来看成第一棵树的另一种拆分方案,因此存在计重问题。

在前一个限制下,用任意合法方案减去强行钦定首尾同色的合法方案,得到第一棵树的 EGF

\[\sum\limits_{i = 1}^{k}f_i\times (i - 1)! \times \sum\limits_{j = 0}^{i - 1}(-1)^{j}\binom{i - 1}{j}\frac{x^{i - j - 1}}{(i - j - 1)!} - \sum\limits_{i = 1}^{k}f_i\times (i - 1)! \times \sum\limits_{j = 0}^{i - 1}(-1)^{j}\binom{i - 1}{j}\frac{x^{i - j - 2}}{(i - j - 2)!} \]

注意后者最后一条链的选取有 \(i - 1\) 种方案,因此其前面的系数仍为 \((i - 1)!\) 而非 \((i - 2)!\)

暴力卷积,最后把每一项的系数乘对应的阶乘(化成 EGF 的形式)。

#include<bits/stdc++.h>
#define LL long long
#define DB double
#define MOD 998244353
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define MP make_pair
#define VI vector<int>
#define VII vector<int>::iterator
#define EB emplace_back
#define SI set<int>
#define SII set<int>::iterator
#define QI queue<int>
using namespace std;
template<typename T> void chkmn(T &a, const T &b) { (a > b) && (a = b); }
template<typename T> void chkmx(T &a, const T &b) { (a < b) && (a = b); }
int inc(const int &a, const int &b) { return a + b >= MOD ? a + b - MOD : a + b; }
int dec(const int &a, const int &b) { return a - b < 0 ? a - b + MOD : a - b; }
int mul(const int &a, const int &b) { return 1LL * a * b % MOD; }
void Inc(int &a, const int &b) { ((a += b) >= MOD) && (a -= MOD); }
void Dec(int &a, const int &b) { ((a -= b) < 0) && (a += MOD); }
void Mul(int &a, const int &b) { a = 1LL * a * b % MOD; }
void Sqr(int &a) { a = 1LL * a * a % MOD; }
int qwqmi(int x, int k = MOD - 2)
{
    int res = 1;
    while(k)
    {
        if(k & 1) Mul(res, x);
        Sqr(x), k >>= 1;
    }
    return res;
}
const int N = 5e3 + 5;
int fac[N], facinv[N];
void preprocess()
{
    fac[0] = facinv[0] = 1;
    for(int i = 1; i < N; ++i)
        fac[i] = mul(fac[i - 1], i);
    facinv[N - 1] = qwqmi(fac[N - 1]);
    for(int i = N - 2; i >= 1; --i)
        facinv[i] = mul(facinv[i + 1], i + 1);
}
int binom(int n, int m)
{
    return mul(fac[n], mul(facinv[m], facinv[n - m]));
}
int m, n, sum;
LL f[N]; // f[i]: 把当前树拆成 i 条链的方案数(考虑排列在路径上的正反顺序)
struct Lexington
{
    int e, ne;
};
struct Sakawa
{
    int idx, h[N];
    Lexington lxt[2 * N];
    int sz[N], dp[N][N][3], tmp[N][3];
    // dp[u][i] : 考虑将 u 子树划分为 i 条已完结的链. 0/1/2 表示 u 所在的链处于什么状态
    // 0 : 已拼完,无法再延伸
    // 1 : 单点,可继续向上扩展
    // 2 : >1 的链,且可继续向上扩展 (为了处理正反向方案数 × 2 的问题)
    // 对于根节点视作它可以向父节点扩展,即和其它节点同等处理
    // 状态由划分的客观形态决定 不能对 1/2 情况人为钦定为 0 
    // f[i] = dp[root][i][0] + dp[root][i - 1][1] + 2 * dp[root][i - 1][2]
    void add(int a, int b)
    {
        lxt[++idx] = (Lexington){b, h[a]};
        h[a] = idx;
    }
    void adds(int a, int b)
    {
        add(a, b), add(b, a);
    }
    void clear()
    {
        idx = 0;
        for(int i = 1; i <= n; ++i)
        {
            h[i] = 0;
            for(int j = 0; j <= sz[i]; ++j)
            {
                dp[i][j][0] = 0;
                dp[i][j][1] = 0;
                dp[i][j][2] = 0;
            }
        }
    }
    void DPtrans(int u, int v)
    {
        for(int i = 0; i <= sz[u]; ++i)
            for(int j = 0; j <= sz[v]; ++j)
            {
                Inc(tmp[i + j][0], mul(dp[u][i][0], dp[v][j][0]));
                Inc(tmp[i + j][1], mul(dp[u][i][1], dp[v][j][0]));
                Inc(tmp[i + j][2], mul(dp[u][i][1], dp[v][j][1]));
                Inc(tmp[i + j][2], mul(dp[u][i][1], dp[v][j][2]));
                Inc(tmp[i + j][2], mul(dp[u][i][2], dp[v][j][0]));
                Inc(tmp[i + j + 1][0], mul(dp[u][i][0], dp[v][j][1]));
                Inc(tmp[i + j + 1][0], mul(2, mul(dp[u][i][0], dp[v][j][2])));
                Inc(tmp[i + j + 1][0], mul(2, mul(dp[u][i][2], dp[v][j][1])));
                Inc(tmp[i + j + 1][0], mul(2, mul(dp[u][i][2], dp[v][j][2])));
                Inc(tmp[i + j + 1][1], mul(dp[u][i][1], dp[v][j][1]));
                Inc(tmp[i + j + 1][1], mul(2, mul(dp[u][i][1], dp[v][j][2])));
                Inc(tmp[i + j + 1][2], mul(dp[u][i][2], dp[v][j][1]));
                Inc(tmp[i + j + 1][2], mul(2, mul(dp[u][i][2], dp[v][j][2])));
            }
        sz[u] += sz[v];
        for(int i = 0; i <= sz[u]; ++i)
        {
            dp[u][i][0] = tmp[i][0], tmp[i][0] = 0;
            dp[u][i][1] = tmp[i][1], tmp[i][1] = 0;
            dp[u][i][2] = tmp[i][2], tmp[i][2] = 0;
        }
    }
    void dfs(int u, int fa)
    {
        sz[u] = 1;
        dp[u][0][1] = 1;
        for(int i = h[u]; i; i = lxt[i].ne)
        {
            int v = lxt[i].e;
            if(v == fa) continue;
            dfs(v, u);
            DPtrans(u, v);
        }
    }
}skw;
int F[N], G[N], H[N], ans;
int main()
{
    preprocess();
    scanf("%d", &m);
    F[0] = 1;
    for(int cas = 1; cas <= m; ++cas)
    {
        scanf("%d", &n);
        for(int i = 1; i < n; ++i)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            skw.adds(a, b);
        }
        skw.dfs(1, 0);
        for(int i = 1; i <= n; ++i)
            f[i] = inc(inc(skw.dp[1][i][0], skw.dp[1][i - 1][1]), mul(2, skw.dp[1][i - 1][2]));
        for(int i = 0; i <= n; ++i)
            G[i] = 0;
        if(cas == 1)
        {
            for(int i = 1; i <= n; ++i)
            {
                int c = mul(f[i], fac[i - 1]);
                for(int j = 0; j < i; ++j)
                {
                    int w = mul(c, mul(binom(i - 1, j), facinv[i - j - 1]));
                    if(j & 1) Dec(G[i - j - 1], w);
                    else Inc(G[i - j - 1], w);
                }
            }
            for(int i = 1; i <= n; ++i)
            {
                int c = mul(f[i], fac[i - 1]);
                for(int j = 0; j < i - 1; ++j)
                {
                    int w = mul(c, mul(binom(i - 1, j), facinv[i - j - 2]));
                    if(j & 1) Inc(G[i - j - 2], w);
                    else Dec(G[i - j - 2], w);
                }
            }
        }
        else 
        {
            for(int i = 1; i <= n; ++i)
            {
                int c = mul(f[i], fac[i]);
                for(int j = 0; j < i; ++j)
                {
                    int w = mul(c, mul(binom(i - 1, j), facinv[i - j]));
                    if(j & 1) Dec(G[i - j], w);
                    else Inc(G[i - j], w);
                }
            }
        }
        for(int i = 0; i <= sum + n; ++i)
            H[i] = 0;
        for(int i = 0; i <= sum; ++i)
            for(int j = 0; j <= n; ++j)
                Inc(H[i + j], mul(F[i], G[j]));
        sum += n;
        for(int i = 0; i <= sum; ++i)
            F[i] = H[i];
        skw.clear();
    }
    for(int i = 0; i <= sum; ++i)
        Inc(ans, mul(F[i], fac[i]));
    printf("%d\n", ans);
    return 0;
}
posted @ 2023-06-07 11:40  Schucking_Sattin  阅读(14)  评论(0编辑  收藏  举报