// TLE 区間DP 苦行 #include #include #include #include #include using mint = atcoder::modint998244353; struct SubtreeSize { SubtreeSize(int n, const std::vector>& g): _n(n), _par(_n, -1), _siz(_n, 1) { auto dfs = [&](auto dfs, int u, int p) -> int { _par[u] = p; for (int v : g[u]) if (v != p) { _siz[u] += dfs(dfs, v, u); } return _siz[u]; }; dfs(dfs, 0, -1); } // u の親を p としたときの、部分木 u のサイズ int operator()(int u, int p) const { return _par[u] == p ? _siz[u] : _n - _siz[p]; } // 解説の t (隣接点が ng1 の場合) int t(int u, int ng1) const { return _n - (*this)(ng1, u); } // 解説の t (隣接点が ng1, ng2 の場合) int t(int u, int ng1, int ng2) const { return _n - (*this)(ng1, u) - (*this)(ng2, u); } private: int _n; std::vector _par, _siz; }; int edge_num(int n) { return (n * (n + 1)) >> 1; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); auto solve = [&]{ int n; std::cin >> n; std::vector> g(n); for (int i = 0; i < n - 1; ++i) { int u, v; std::cin >> u >> v; --u, --v; g[u].push_back(v); g[v].push_back(u); } const int m = edge_num(n); const mint inv_m = mint(m).inv(); SubtreeSize subtree_size { n, g }; std::vector ans_f(n, 0); for (int x = 0; x < n; ++x) { int u_x = edge_num(n); for (int y : g[x]) { u_x -= edge_num(subtree_size(y, x)); } ans_f[x] = m * mint(m - u_x).inv(); } std::vector> ans_g(n, std::vector(n)); // par[x][y] := x を根とする木における y の親 std::vector> par(n, std::vector(n, -1)); // x, y, A_{x,y}, B_{x,y} std::deque> dq; for (int x = 0; x < n; ++x) { ans_g[x][x] = ans_f[x]; // s_x(x) const int s_x_x = n; // u_{x,x}(x) int u_xx_x = edge_num(n); for (int y : g[x]) { // s_x(y) const int s_x_y = subtree_size(y, x); u_xx_x -= edge_num(s_x_y); } for (int y : g[x]) { // s_x(y) const int s_x_y = subtree_size(y, x); // u_{x,y}(x) const int u_xy_x = u_xx_x - s_x_y * (s_x_x - s_x_y); const mint Axy = u_xy_x * ans_f[x]; const mint Bxy = 0; par[x][y] = x; dq.emplace_back(x, y, Axy, Bxy); } } while (dq.size()) { auto [x, y, Axy, Bxy] = dq.front(); dq.pop_front(); // x を根とした木における y の親 const int par_y = par[x][y]; // s_x(y) const int s_x_y = subtree_size(y, par_y); // u_{x,y}(y) int u_xy_y = edge_num(s_x_y); for (int w : g[y]) if (w != par_y) { u_xy_y -= edge_num(subtree_size(w, y)); } // t_{x,y}(y) const int t_xy_y = s_x_y; ans_g[x][y] = Axy + u_xy_y * ans_f[y] + Bxy; // sum _ {z in Pxy-{y}} t_{x,y}(y) t_{x,y}(z) g(y,z) の計算 int prev_z = y, z = par_y; while (z != x) { const int next_z = par[x][z]; // t_{x,y}(z) // z の 1 つ前と 1 つ後が N_{x,y}(z) に含まれる頂点 const int t_xy_z = subtree_size.t(z, prev_z, next_z); ans_g[x][y] += t_xy_y * t_xy_z * ans_g[y][z]; std::tie(prev_z, z) = std::make_tuple(z, next_z); } // t_{x,y}(x) const int t_xy_x = subtree_size.t(x, prev_z); ans_g[x][y] = (1 + ans_g[x][y] * inv_m) * (1 - t_xy_x * t_xy_y * inv_m).inv(); for (int w : g[y]) if (w != par_y) { // t_{x,w}(x) const int t_xw_x = t_xy_x; // s_{x}(w) const int s_x_w = subtree_size(w, y); // t_{x,w}(y) const int t_xw_y = t_xy_y - s_x_w; // u_{x,w}(y) const int u_xw_y = u_xy_y - s_x_w * (s_x_y - s_x_w); // A_{x,w} const mint Axw = Axy + u_xw_y * ans_f[y]; // B_{x,w} mint Bxw = Bxy + t_xw_y * t_xw_x * ans_g[x][y]; // Bxw に sum_{z in Pxy-{y}} t_{x,w}(y) * t_{x,w}(z) * g(y,z) を足して更新 int prev_z = y, z = par_y; while (z != x) { const int next_z = par[x][z]; // t_{x,w}(z) const int t_xw_z = subtree_size.t(z, prev_z, next_z); // t_{x,w}(y) * t_{x,w}(z) * g(y, z) Bxw += t_xw_y * t_xw_z * ans_g[y][z]; std::tie(prev_z, z) = std::make_tuple(z, next_z); } par[x][w] = y; dq.emplace_back(x, w, Axw, Bxw); } } mint ans = 1; for (int x = 0; x < n; ++x) { for (int y = 0; y <= x; ++y) { ans += ans_g[x][y] * inv_m; } } std::cout << ans.val() << '\n'; }; int t; std::cin >> t; while (t --> 0) { solve(); } return 0; }