「JSOI2019」神经网络

#3102. 「JSOI2019」神经网络

Summarize

给定$m$棵无根树,总结点数为$k$,对于任意两个属于不同的树的点,在形成的图$G$中连一条边,求$G$中的哈密顿回路数

$1 \le m \le 300, 1 \le k \le 5 \times 10^3$

感谢@oy的贡献

Solution

根据题意,合法的哈密顿回路只能从一个点走到同一棵树上相邻的点,或是其他树上的任意点。因此在每一棵树上经过的路径必定是几条不相交的链。可以先计算将树拆分成链的方案数,再将它们合并起来。

将树拆分成链可以用背包类树形 DP 解决。记 $f[u][i][0/1/2]$ 表示将以 $u$ 为根的子树拆分成 $i$ 条有向链的方案数,特殊的,根节点所在的链没有方向。$0$ 表示根节点所在的链长度为 $1$ ,$1$ 表示根节点为所在链的一个端点,$2$ 表示根节点不是其所在链的端点。

大力树形背包转移即可,略显繁琐,详见代码。

接下来考虑将若干条链合并为哈密顿回路。考虑简化后的问题:有若干种颜色的小球,每种颜色的小球个数不定。将所有小球摆成一个环,并且不能有颜色相同的小球位于相邻位置。求方案数。

在本题中,颜色即对应树,小球对应链。构造每一种颜色的生成函数如下:
$$
\sum_{i=1}^{n}f_i i!\sum_{j=1}^{i}(-1)^{i-j} {i-1\choose j-1}\frac{x^j}{j!}
$$
其中 $f_i$ 表示将树划分为 $i$ 条链的方案数。对于保证至少有奇数个同色对的方案,需要乘系数 $-1$。将所有生成函数卷起来,每种系数前做一个环排统计方案即可。

至此 JSOI2019 就全部做完了,还是非常吃力的。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 5005;
const int MAXM = 305;
const ll MOD = 998244353;

struct Edge {
int v, nxt;
}edge[MAXN << 1];
int head[MAXN], cnt;

inline void AddEdge(int u, int v) {
edge[++cnt].v = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}

int n, m;
ll f[MAXN][MAXN][3], t[MAXN][MAXN][3];
int size[MAXN];

void dp(int u, int fa) {
size[u] = 1;
f[u][1][0] = 1;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v;
if (v == fa) continue;
dp(v, u);
for (int i = 1; i <= size[u]; ++i) {
for (int j = 1; j <= size[v]; ++j) {
ll tmp1 = (f[v][j][0] + f[v][j][1] * 2 + f[v][j][2] * 2) % MOD;
ll tmp2 = (f[v][j][0] + f[v][j][1]) % MOD;
t[u][i + j][0] = (t[u][i + j][0] + tmp1 * f[u][i][0]) % MOD;
t[u][i + j][1] = (t[u][i + j][1] + tmp1 * f[u][i][1]) % MOD;
t[u][i + j][2] = (t[u][i + j][2] + tmp1 * f[u][i][2]) % MOD;
t[u][i + j - 1][1] = (t[u][i + j - 1][1] + tmp2 * f[u][i][0]) % MOD;
t[u][i + j - 1][2] = (t[u][i + j - 1][2] + tmp2 * f[u][i][1]) % MOD;
}
}
size[u] += size[v];
for (int i = 1; i <= size[u]; ++i) {
f[u][i][0] = t[u][i][0],
f[u][i][1] = t[u][i][1],
f[u][i][2] = t[u][i][2],
t[u][i][0] = t[u][i][1] = t[u][i][2] = 0;
}
}
}

ll inv[MAXN], fac[MAXN], facinv[MAXN];

inline ll C(int n, int m) {
return fac[n] * facinv[m] % MOD * facinv[n - m] % MOD;
}

ll a[MAXN * MAXM], c[MAXN * MAXM], b[MAXN];
int len;

int main() {
fac[0] = facinv[0] = 1;
inv[1] = fac[1] = facinv[1] = 1;
for (int i = 2; i <= 5000; ++i) {
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
fac[i] = fac[i - 1] * 1ll * i % MOD;
facinv[i] = facinv[i - 1] * inv[i] % MOD;
}
a[0] = 1;
scanf("%d", &m);
for (int cas = 1; cas <= m; ++cas) {
cnt = 0;
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
AddEdge(u, v), AddEdge(v, u);
}
dp(1, 0);
for (int i = 1; i <= n; ++i) {
ll tmp = (f[1][i][0] + f[1][i][1] * 2 + f[1][i][2] * 2) * fac[i] % MOD;
for (int j = 1; j <= i; ++j) {
b[j] = (b[j] + ((i - j) & 1 ? -1 : 1) * tmp * C(i - 1, j - 1) % MOD * facinv[j] % MOD) % MOD;
}
}
for (int i = 0; i <= len; ++i) c[i] = 0;
for (int i = 0; i <= len; ++i)
for (int j = 1; j <= n; ++j)
c[i + j] = (c[i + j] + a[i] * b[j] % MOD) % MOD;
len += n;
for (int i = 0; i <= len; ++i) a[i] = c[i];
for (int i = 0; i <= n; ++i) {
head[i] = 0;
b[i] = 0;
for (int j = 0; j <= n; ++j) {
f[i][j][0] = f[i][j][1] = f[i][j][2] = 0;
t[i][j][0] = t[i][j][1] = t[i][j][2] = 0;
}
}
}
ll ans = 0;
for (int i = 0; i <= len; ++i)
ans = (ans + a[i] * fac[i] % MOD * inv[i] % MOD + MOD) % MOD;
printf("%lld", ans);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×