P5333 [JSOI2019] 神经网络 【树形dp,EGF】
题目描述:给你 \(m\) 棵树,第 \(i\) 棵有 \(k_i\) 个节点。将这 \(m\) 棵树放在一起,任意两棵树之间连成完全二分图,得到了一个 \(\sum k_i\) 个点的无向简单联通图,求哈密顿回路个数。
数据范围:\(m\le 300,\sum k_i\le 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_i\sum_{j=0}^{i-1}\frac{(-1)^j}{j!}\cdot\frac{x^{i-j}}{(i-j)!(i-j-1)!}
\]
对于第一棵树,第一条链不应参与排列,而且最后一条链不能在第一棵树里。
\[\begin{aligned}F&=\sum_{i=1}^k(i-1)!^2f_i\sum_{j=0}^{i-1}\frac{(-1)^j}{j!}\cdot\frac{x^{i-j-1}}{(i-j-1)!^2} \\&-\sum_{i=1}^k(i-1)!^2f_i\sum_{j=0}^{i-2}\frac{(-1)^j}{j!(i-j-1)!}\cdot\frac{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\n", res);
}