Address
Solution
容易发现,一条哈密顿回路本质上就是:把每棵树都拆成若干条有向路径,再把所有的有向路径连接成环,环上的相邻两条有向路径不可以来自同一棵树。
先求出 gi,j 表示把第 i 棵树拆成 j 条有向路径的方案数。
考虑 dp,记 fu,i,0/1/2/3 分别表示:u 的子树拆成 i 条路径,u 是路径起点,是路径终点,单点成路径,既不是路径起点也不是路径终点的方案数。
注意 fu,i,0/1 不允许 u 单点成路径。转移随便讨论一下即可。最终 gi,j=fu,i,0+fu,i,1+fu,i,2+fu,i,3。
接下来,假设我们对于所有的 i∈[1,m],已经确定第 i 棵树拆成 ai 条链】路径,那么如何计算答案呢?
考虑容斥。枚举第 i 棵树的路径在环上被划分为至多 bi 段。我们钦定第 m 棵树的 1 号节点所在的路径为环上第一条路径,那么在此条件下的方案数为:
(i=1∏mCai−1bi−1)×(i=1∏m−1gi,ai×ai!)×gm,am×(am−1)!×∏i=1m−1bi!(∑i=1m−1bi)!×C(∑i=1nbi)−2bm−1其中 ∏i=1mCai−1bi−1 表示把每棵树的路径划分成 i 段的方案数,gi,ai×ai! 表示在第 i 棵树上选出 ai 条路径形成一个排列的方案数。
如果确定了选出的路径形成的排列是哪些,划分成的 bi 段分别是什么,问题就转化为:第 i 种颜色的球有 bi 个,同种颜色的球之间没有区别,求把所有的求串成环,使得相邻两个异色的方案数。
钦定环上第一条路径相当于钦定第一个球的颜色一定为 m,最后一个球的颜色不是 m,然后断环为链。所以先把前 m−1 种颜色的球排好(方案数为),最后在中间的 (∑bi)−2 个位置中选出 bm−1 个位置放颜色为 m 的球,剩下的空位给其它的球即可。
加上容斥系数之后对答案的贡献即(i=1∏mCai−1bi−1)×(−1)∑i=1mai−bi×(i=1∏m−1gi,ai×ai!)×gm,am×(am−1)!×∏i=1m−1bi!(∑i=1m−1bi)!×C(∑i=1nbi)−2bm−1
可是直接枚举所有 ai,bi 的复杂度是指数级的,考虑优化。
记第 i 棵树的点数为 cnti。对于第 i 棵树(i∈[1,m−1]),枚举 j=ai,k=bi,可以写出这样的一个多项式:
j=1∑cntigi,j×i!k=1∑jCj−1k−1×(−1)j−k×k!xk
然后我们先把这 m−1 个多项式相乘,再把 xk 的系数乘上 k!。这样得到的 xk 的系数 Ak 相当于:对于 i∈[1,m−1],枚举所有 ai,bi 满足 ∑i=1m−1bi=k,然后把 (i=1∏mCai−1bi−1)×(−1)∑i=1mai−bi×(i=1∏m−1gi,ai×ai!)×∏i=1m−1bi!(∑i=1m−1bi)! 计入 Ak。
接下来写出第 m 个多项式,记 Bk 表示这个多项式第 k 项的系数:
j=1∑cntmgm,j×(j−1)!×k=1∑jCj−1k−1×(−1)j−k×xk
最后枚举 k=∑i=1m−1bi,再枚举 j=bm,把 Ak×Bj×Ck+j−2j−1 计入答案即可。
时间复杂度 O((∑cnti)2)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
const int e = 5005, o = 305, mod = 998244353;
int g[o][e], f[e][e][4], sze[e], m, n, ans, tmp[e][4], cnt, pre[e], h[e], p[o][e];
int adj[e], nxt[e << 1], go[e << 1], num, tot[o], fac[e], inv[e], a[o][e];
inline void add(int &x, int y)
{
(x += y) >= mod && (x -= mod);
}
inline int plu(int x, int y)
{
add(x, y);
return x;
}
inline int mul(int x, int y)
{
return (ll)x * y % mod;
}
inline int ksm(int x, int y)
{
int res = 1;
while (y)
{
if (y & 1) res = mul(res, x);
y >>= 1;
x = mul(x, x);
}
return res;
}
inline void link(int x, int y)
{
nxt[++num] = adj[x]; adj[x] = num; go[num] = y;
nxt[++num] = adj[y]; adj[y] = num; go[num] = x;
}
inline int c(int x, int y)
{
if (x < y) return 0;
if (x == y) return 1;
return mul(fac[x], mul(inv[y], inv[x - y]));
}
inline void clear()
{
int i, j, k; num = ans = 0;
for (i = 1; i <= n; i++)
{
adj[i] = 0;
for (j = 1; j <= n; j++)
for (k = 0; k <= 3; k++)
f[i][j][k] = 0;
}
}
inline void dfs(int u, int pa)
{
sze[u] = f[u][1][2] = 1;
int i, j, k, l;
for (i = adj[u]; i; i = nxt[i])
{
int v = go[i];
if (v == pa) continue;
dfs(v, u);
for (j = 1; j <= sze[u] + sze[v]; j++)
for (k = 0; k <= 3; k++)
tmp[j][k] = f[u][j][k], f[u][j][k] = 0;
for (j = 1; j <= sze[u]; j++)
for (k = 1; k <= sze[v]; k++)
{
int s = plu(f[v][k][0], f[v][k][2]);
add(f[u][j + k - 1][0], mul(tmp[j][2], s));
add(f[u][j + k - 1][3], mul(tmp[j][1], s));
s = plu(f[v][k][1], f[v][k][2]);
add(f[u][j + k - 1][1], mul(tmp[j][2], s));
add(f[u][j + k - 1][3], mul(tmp[j][0], s));
s = 0;
for (l = 0; l <= 3; l++)
add(s, f[v][k][l]);
for (l = 0; l <= 3; l++)
add(f[u][j + k][l], mul(tmp[j][l], s));
}
sze[u] += sze[v];
}
}
inline void solve(int k)
{
read(n); clear();
cnt += n; tot[k] = n; pre[k] = tot[k] + pre[k - 1];
int i, x, y, j;
for (i = 1; i < n; i++)
read(x), read(y), link(x, y);
dfs(1, 0);
for (i = 1; i <= n; i++)
for (j = 0; j <= 3; j++)
add(g[k][i], f[1][i][j]);
}
int main()
{
read(m);
int i, j, k;
for (i = 1; i <= m; i++)
solve(i);
fac[0] = 1;
for (i = 1; i <= cnt; i++)
fac[i] = mul(fac[i - 1], i);
inv[cnt] = ksm(fac[cnt], mod - 2);
for (i = cnt - 1; i >= 0; i--)
inv[i] = mul(inv[i + 1], i + 1);
for (i = 1; i < m; i++)
for (j = 1; j <= tot[i]; j++)
g[i][j] = mul(g[i][j], fac[j]);
for (j = 1; j <= tot[m]; j++)
g[m][j] = mul(g[m][j], fac[j - 1]);
for (i = 1; i < m; i++)
for (j = 1; j <= tot[i]; j++)
for (k = 1; k <= j; k++)
{
int v = mul(g[i][j], c(j - 1, k - 1));
v = mul(v, inv[k]);
if ((j - k) & 1) add(p[i][k], mod - v);
else add(p[i][k], v);
}
for (j = 1; j <= tot[m]; j++)
for (k = 1; k <= j; k++)
{
int v = mul(g[m][j], c(j - 1, k - 1));
if ((j - k) & 1) add(p[m][k], mod - v);
else add(p[m][k], v);
}
a[0][0] = 1;
for (i = 1; i < m; i++)
for (j = 1; j <= pre[i]; j++)
for (k = 1; k <= j && k <= tot[i]; k++)
add(a[i][j], mul(a[i - 1][j - k], p[i][k]));
for (i = 1; i <= pre[m - 1]; i++)
for (j = 1; j <= tot[m]; j++)
{
int x = mul(a[m - 1][i], fac[i]), y = p[m][j];
add(ans, mul(x, mul(y, c(i + j - 2, j - 1))));
}
cout << ans << endl;
return 0;
}
花淇淋
发布了42 篇原创文章 · 获赞 5 · 访问量 3420
私信
关注