P5333 [JSOI2019] 神经网络 树形dp,EGF

Posted AThousandMoons

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P5333 [JSOI2019] 神经网络 树形dp,EGF相关的知识,希望对你有一定的参考价值。

题目链接

题目描述:给你 (m) 棵树,第 (i) 棵有 (k_i) 个节点。将这 (m) 棵树放在一起,任意两棵树之间连成完全二分图,得到了一个 (sum k_i) 个点的无向简单联通图,求哈密顿回路个数。

数据范围:(mle 300,sum k_ile 5000)


首先强制从第 (1) 棵树的 (1) 号节点开始连接,可以看成每次走其中一棵树上面的一条链,然后跨越到另一颗树上去,然后计数这个链的排列。所以答案与每棵树的形态没什么关系,求出 (f_i) 表示当前树划分为 (i) 条链的方案数,这个可以 (O(k^2)) dp 做,设 (dp_{x,i,0/1/2}) 表示以 (x) 为根的子树中,划分为了 (i) 条链,(x) 单独成链/为链端点/为链中间点 的方案数,转移显然...显然么?好吧因为这里的链是有方向的,所以注意有些地方有 (2) 的系数。

然后就要计数这些链的排列,是不是只能用 EGF 了啊... 你可以把每棵树对答案的贡献设为一个 EGF,然后计算答案的时候就把所有 EGF 乘起来就可以了。

对于除了第一棵之外的树,要求同一棵树的两条链不能相邻,所以可以用容斥。

[F=sum_{i=1}^ki!(i-1)!f_isum_{j=0}^{i-1}frac{(-1)^j}{j!}cdotfrac{x^{i-j}}{(i-j)!(i-j-1)!} ]

对于第一棵树,第一条链不应参与排列,而且最后一条链不能在第一棵树里。

[egin{aligned}F&=sum_{i=1}^k(i-1)!^2f_isum_{j=0}^{i-1}frac{(-1)^j}{j!}cdotfrac{x^{i-j-1}}{(i-j-1)!^2} \&-sum_{i=1}^k(i-1)!^2f_isum_{j=0}^{i-2}frac{(-1)^j}{j!(i-j-1)!}cdotfrac{x^{i-j-2}}{(i-j-2)!}end{aligned} ]

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    bool f = false;
    for(;ch < ‘0‘ || ch > ‘9‘;ch = getchar()) f |= ch == ‘-‘;
    for(;ch >= ‘0‘ && ch <= ‘9‘;ch = getchar()) x = x * 10 + ch - ‘0‘;
    if(f) x = -x;
}
const int M = 303, N = 5003, mod = 998244353;
int m, n[M], cnt, f[M][N], siz[N], head[N], to[N << 1], nxt[N << 1], fac[N], inv[N], dp[N][N][3], tmp[N][3], ans[N], nsiz, xx[N], ttt[N], res;
inline void qmo(int &x){x += (x >> 31) & mod;}
inline void add(int a, int b){
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
}
inline int ksm(int a, int b){
    int res = 1;
    for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
    return res;
}
inline void init(int m){
    fac[0] = 1;
    for(Rint i = 1;i <= m;++ i) fac[i] = (LL) fac[i - 1] * i % mod;
    inv[m] = ksm(fac[m], mod - 2);
    for(Rint i = m;i;-- i) inv[i - 1] = (LL) inv[i] * i % mod;
}
inline void dfs(int x, int f = 0){
    siz[x] = dp[x][1][0] = 1;
    for(Rint i = head[x];i;i = nxt[i]) if(to[i] != f){
        dfs(to[i], x);
        for(Rint j = 1;j <= siz[x] + siz[to[i]];++ j) tmp[j][0] = tmp[j][1] = tmp[j][2] = 0;
        for(Rint j = 1;j <= siz[x];++ j)
            for(Rint k = 1;k <= siz[to[i]];++ k){
                int ttt = (dp[to[i]][k][0] + 2ll * dp[to[i]][k][1] + dp[to[i]][k][2]) % mod, tt = dp[to[i]][k][0] + dp[to[i]][k][1]; qmo(tt -= mod);
                qmo(tmp[j + k][0] += (LL) dp[x][j][0] * ttt % mod - mod);
                qmo(tmp[j + k][1] += (LL) dp[x][j][1] * ttt % mod - mod);
                qmo(tmp[j + k][2] += (LL) dp[x][j][2] * ttt % mod - mod);
                qmo(tmp[j + k - 1][1] += (LL) dp[x][j][0] * tt % mod - mod);
                qmo(tmp[j + k - 1][2] += 2ll * dp[x][j][1] * tt % mod - mod);
            }
        siz[x] += siz[to[i]];
        for(Rint j = 1;j <= siz[x];++ j)
            for(Rint k = 0;k < 3;++ k){dp[x][j][k] = tmp[j][k]; tmp[j][k] = 0;}
    }
}
inline int C(int n, int m){return (LL) fac[n] * inv[m] % mod * inv[n - m] % mod;}
int main(){
    read(m); if(m <= 1) return puts("0"), 0; init(5000);
    for(Rint t = 1;t <= m;++ t){
        read(n[t]); cnt = 0; memset(head, 0, sizeof head);
        for(Rint i = 1, u, v;i < n[t];++ i){
            read(u); read(v); add(u, v); add(v, u);
        }
        dfs(1);
        for(Rint i = 1;i <= n[t];++ i) f[t][i] = (dp[1][i][0] + 2ll * dp[1][i][1] + dp[1][i][2]) % mod;
        for(Rint i = 1;i <= n[t];++ i)
            for(Rint j = 1;j <= n[t];++ j) dp[i][j][0] = dp[i][j][1] = dp[i][j][2] = 0;
    }
    nsiz = n[1];
    for(Rint i = 1;i <= n[1];++ i){
        for(Rint j = 0;j < i;++ j){
            int tmp = (LL) f[1][i] * fac[i - 1] % mod * C(i - 1, j) % mod;
            if(j & 1) qmo(ans[i - j - 1] -= tmp); else qmo(ans[i - j - 1] += tmp - mod);
        }
        for(Rint j = 0;j < i - 1;++ j){
            int tmp = (LL) f[1][i] * fac[i - 1] % mod * C(i - 1, j) % mod;
            if(j & 1) qmo(ans[i - j - 2] += tmp - mod); else qmo(ans[i - j - 2] -= tmp);
        }
    }
    for(Rint i = 0;i <= n[1];++ i) ans[i] = (LL) ans[i] * inv[i] % mod;
    for(Rint t = 2;t <= m;++ t){
        for(Rint j = 0;j <= n[t];++ j) xx[j] = 0;
        for(Rint i = 1;i <= n[t];++ i)
            for(Rint j = 0;j < i;++ j){
                int tmp = (LL) f[t][i] * fac[i] % mod * C(i - 1, j) % mod;
                if(j & 1) qmo(xx[i - j] -= tmp); else qmo(xx[i - j] += tmp - mod);
            }
        for(Rint i = 0;i <= n[t];++ i) xx[i] = (LL) xx[i] * inv[i] % mod;
        for(Rint i = 0;i <= nsiz;++ i)
            for(Rint j = 0;j <= n[t];++ j)
                qmo(ttt[i + j] += (LL) ans[i] * xx[j] % mod - mod);
        nsiz += n[t];
        for(Rint i = 0;i <= nsiz;++ i){ans[i] = ttt[i]; ttt[i] = 0;}
    }
    for(Rint i = 0;i <= nsiz;++ i) qmo(res += (LL) ans[i] * fac[i] % mod - mod);
    printf("%d
", res);
}

以上是关于P5333 [JSOI2019] 神经网络 树形dp,EGF的主要内容,如果未能解决你的问题,请参考以下文章

2019 沈阳网络赛 D Fish eating fruit ( 树形DP)

[JSOI2019]神经网络

[JSOI 2016] 最佳团体(树形背包+01分数规划)

[BZOJ1017][JSOI2008]魔兽地图DotR 树形dp

bzoj 1017[JSOI2008]魔兽地图DotR - 树形dp

JSOI Salesman 树形Dp