Algorithm: min-max inclusion and exclusion, tree backpack, NTT.
Description of the meaning of problems
There are a \ (n \) tree points. All start points are white, each operation will randomly select \ (\ frac {n \ times (n + 1)} {2} \) of paths a, all points on the path to black. All the expected number of points are seeking operation is blacked out.
\ (the n-\ Le 50 \) . Multiple sets of data. For \ (998, 244, 353 \) modulo.
answer
Routine manner, we use the min-max inclusion and exclusion.
\ [\ Begin {aligned} E (\ max (U)) & = \ sum _ {\ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} E (\ min (S) ) \\ & = \ sum _ { \ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} \ frac {1} {P ( route through \ S \ point inside)} \ \ & = \ sum _ {\ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} \ frac {1} {1 - P ( the path without passing through the point \ S \ in)} \ end {aligned} \]
If we draw out the tree, and \ (S \) the tags in, can be found, the points of the original tree into a plurality of blocks of communication, and communication with the interior of each block may be chosen arbitrarily path, to ensure that the path does not pass through \ (S \) points therein. Once the communication path across the block, then it must go through \ (S \) points therein.
Referred to would be divided into \ (X \) interconnecting blocks \ (T_l, T_2, \ cdots, T_x \) , without passing \ (S \) path number
\ [\ sum_ {i = 1 } ^ {x} \ frac {\ lvert T_i \ rvert
\ times \ left (\ lvert T_i \ rvert + 1 \ right)} {2} \] then the probability
\ [\ frac {\ sum_ { i = 1} ^ {x} \ lvert t_i \ rvert \ times \ left (
\ lvert t_i \ rvert + 1 \ right)} {n \ times (n + 1)} \] continue to simplify the original formula to give
\ [\ Begin {aligned} E (\ max (U)) & = \ sum _ {\ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} \ frac {1} {1 - \ dfrac {\ sum_ {i = 1} ^ {x} \ lvert T_i \ rvert \ times \ left (\ lvert T_i \ rvert + 1 \ right)} {n \ times (n + 1)}} \\ & = \ sum _ {\ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} \ frac {n \ times (n + 1)} {n \ times (n + 1) - \ sum_ {i = 1} ^ { x} \ lvert T_i \ rvert \ times \ left (\ lvert T_i \ rvert + 1 \ right)} \\ & = \ frac {n \ times (n + 1)} {2} \ sum _ {\ phi \ neq S \ subset U} (-1) ^ {\ lvert S \ rvert - 1} \ frac {1} {\ dfrac {n \ times (n + 1)} {2} - \ sum_ {i = 1} ^ {x
} \ dfrac {\ lvert T_i \ rvert \ times \ left (\ lvert T_i \ rvert + 1 \ right)} {2}} \ end {aligned} \] for the final case, one division contribution scheme to bring answers only \ ((- 1) ^ { | S | - 1} \) and \ (\ sum_ {i = 1 } ^ x \ dfrac {\ lvert T_i \ rvert \ times \ left ( \ lvert T_i \ rvert + 1 \ right)} {2} \)Two. To gain this information, we are only interested in the number of points selected odd, even, in the embodiment, the number of programs \ (\ sum_ {i = 1 } ^ x \ dfrac {\ lvert T_i \ rvert \ times \ left ( \ lvert T_i \ rvert + 1 \ right)} {2} \) for my enum constant \ (K \) .
Consider the design of a dynamic programming to solve this problem. We first select \ (1 \) dots as the root of the whole tree, the tree to become a rooted tree.
Note \ (f [i] [odd ] [j] [k] \) represents only considered to \ (I \) subtree rooted, selected from the odd / even number of points, comprising \ (I \) communicates block size \ (J \) , \ (\ sum_ {I =. 1} ^ X \ dfrac {\ lvert T_i \ rvert \ Times \ left (\ lvert T_i \ rvert +. 1 \ right)} {2} \) ( free \ (I \) where the value of the communication block) \ (K \) number scheme. Wherein \ (j = 0 \) represents \ (I \) dot block is not communicating (i.e., focused at the selected point).
Further, we denote an array \ (g [i] [odd ] [k] \) represented by \ (I \) is the root of the subtree is selected from the odd / even dot, \ (\ sum_ {I =. 1} X ^ \ dfrac {\ lvert T_i \ rvert \ Times \ left (\ lvert T_i \ rvert +. 1 \ right)} {2} \) (containing \ (I \) is located in communication block) \ (K \) the number of programs. Clearly
\ [g [i] [odd ] [k '] = \ sum_ {j = 0} ^ n \ sum_ {k = 0} ^ {\ frac {n \ times (n + 1)} {2}} \ left [\ frac {j \ times (j + 1)} {2} + k == k '\ right] f [i] [odd] [j] [k] \]
Transfer backpack similar to the tree. The combined \ (I \) and a son \ (V \) , the metastasis of formula:
\ [F [I] [ODD] [J] [K] = \ left \ {\ the begin {the aligned} & \ sum_ {ODD '\ in \ {0, 1 \}} \ sum_ {k' = 0} ^ {k} f [i] [odd] [j] [k '] \ cdot g [v] [odd \ oplus odd'] [k - k '] \ quad & j = 0 \\ & \ sum_ {odd' \ in \ {0, 1 \}} \ sum_ {j '= 1} ^ {j} \ sum_ {k' = 0} ^ {k} f [i] [odd] [j] [k] \ cdot f [v] [odd \ oplus odd '] [j - j'] [k - k '] \ quad & j \ neq 0 \ end {aligned} \ right. \
] transfer complexity violence \ (\ mathcal {O} ( n ^ 7) \) and unacceptable.
We note that \ (j \) the size of a dimension that does not exceed \ (i \) of the sub-tree size, this is the classic form of the tree backpack. Thus a less complexity \ (n-\) , becomes \ (\ mathcal {O} (n-^. 6) \) .
Continue to observe, it was found \ (K \) transfer is a one-dimensional convolution form, NTT may be used for optimization. In other words, in addition to the \ (f [i] \) to \ (g [i] \) where the calculated contribution, if we take as the last dimension of a state \ (\ frac {n \ times (n + 1)} {2} \) polynomial of degree, then all of this can be viewed as a polynomial computation polynomial multiplication and addition. So we can just represent a point value at the beginning of Act represents \ (f \) and \ (g \) values only in \ (f \) to \ (g \) when the count contributions, we conduct a INTT , the value represents a reduction back to point coefficient representation, to give \ (G \) after, and again can be reduced to the point value representation.
So the original \ (\ mathcal {O} ( n ^ 2) \) transfer is optimized to \ (\ mathcal {O} (\ log n-) \) . After this overall complexity becomes \ (\ mathcal {O} (n-^. 4 \ log n-) \) .
When the test data of each program run I \ (0.5 \ text {S} \) , the number of data sets that question \ (T = 15 \) after the card I was constant. At present only the former had \ (9 \) points, the last \ (3 \) points are still struggling in the regular card.
#include <algorithm>
#include <cstdio>
#include <cstring>
const int MaxN = 50 + 5;
const int MaxV = 4096 + 5;
const int Mod = 998244353, Prt = 3;
struct Graph {
int cnte;
int Head[MaxN], To[MaxN * 2], Next[MaxN * 2];
inline void clear() {
cnte = 0;
memset(Head, 0, sizeof Head);
memset(To, 0, sizeof To);
memset(Next, 0, sizeof Next);
}
inline void addEdge(int from, int to) {
cnte++; To[cnte] = to;
Next[cnte] = Head[from]; Head[from] = cnte;
}
};
int Te, N;
int Fa[MaxN], Siz[MaxN];
int F[MaxN][2][MaxN][MaxV], G[MaxN][2][MaxV];
int Rev[MaxV][MaxV], W[2][MaxV], Inv[MaxV];
Graph Gr;
inline int add(int x, int y) { return (x += y) >= Mod ? x - Mod : x; }
inline int sub(int x, int y) { return (x -= y) < 0 ? x + Mod : x; }
inline int mul(int x, int y) { return 1LL * x * y % Mod; }
inline int pw(int x, int y) { int z = 1; for (; y; y >>= 1, x = mul(x, x)) if (y & 1) z = mul(z, x); return z; }
inline int inv(int x) { return pw(x, Mod - 2); }
inline int sep(int x, int y) { return mul(x, inv(y)); }
inline void inc(int &x, int y = 1) { x = add(x, y); }
inline void dec(int &x, int y = 1) { x = sub(x, y); }
void init() {
scanf("%d", &N);
for (int i = 1; i < N; ++i) {
int u, v;
scanf("%d %d", &u, &v);
Gr.addEdge(u, v);
Gr.addEdge(v, u);
}
}
void dfs1(int u) {
Siz[u] = 1;
for (int i = Gr.Head[u]; i; i = Gr.Next[i]) {
int v = Gr.To[i];
if (v == Fa[u]) continue;
Fa[v] = u;
dfs1(v);
Siz[u] += Siz[v];
}
}
inline void ntt(int *a, int n, int f) {
for (int i = 1; i < n; ++i)
if (i < Rev[n][i]) std::swap(a[i], a[Rev[n][i]]);
for (int i = 1; i < n; i <<= 1) {
int w = W[f][i];
for (int j = 0; j < n; j += (i << 1)) {
int x = 1;
for (int k = 0; k < i; ++k, x = mul(x, w)) {
int lson = a[j + k], rson = a[i + j + k];
a[j + k] = add(lson, mul(rson, x));
a[i + j + k] = sub(lson, mul(rson, x));
}
}
}
if (f == 1)
for (int i = 0; i < n; ++i) a[i] = mul(a[i], Inv[n]);
}
inline int getPow2(int n) {
int v = 1;
while (v < n) v <<= 1;
return v;
}
inline void calcG(int u) {
for (int odd = 0; odd <= 1; ++odd) {
for (int j = 0; j <= N; ++j)
for (int k = 0; k <= N * (N + 1) / 2; ++k) {
int newK = j * (j + 1) / 2 + k;
if (newK > N * (N + 1) / 2) break;
inc(G[u][odd][newK], F[u][odd][j][k]);
}
}
}
void dfs2(int u) {
F[u][1][0][0] = F[u][0][1][0] = 1;
int sz = 1;
for (int i = Gr.Head[u]; i; i = Gr.Next[i]) {
int v = Gr.To[i];
if (v == Fa[u]) continue;
dfs2(v);
sz += Siz[v];
static int f[2][MaxN][MaxV];
int len = getPow2(Siz[u] * (Siz[u] + 1));
for (int odd = 0; odd <= 1; ++odd)
for (int j = 0; j <= sz; ++j)
for (int k = 0; k < len; ++k)
f[odd][j][k] = 0;
for (int odd = 0; odd <= 1; ++odd) {
ntt(G[v][odd], len, 0);
for (int j = 0; j <= sz - Siz[v]; ++j) ntt(F[u][odd][j], len, 0);
for (int j = 0; j <= Siz[v]; ++j) ntt(F[v][odd][j], len, 0);
}
for (int odd = 0; odd <= 1; ++odd) {
for (int j = 0; j <= sz; ++j) {
for (int odd2 = 0; odd2 <= 1; ++odd2) {
if (j == 0) {
for (int k = 0; k < len; ++k)
inc(f[odd][j][k], mul(F[u][odd ^ odd2][j][k], G[v][odd2][k]));
} else {
for (int j2 = std::max(0, j - sz + Siz[v]); j2 <= Siz[v] && j2 < j; ++j2) {
for (int k = 0; k < len; ++k)
inc(f[odd][j][k], mul(F[u][odd ^ odd2][j - j2][k], F[v][odd2][j2][k]));
}
}
}
}
}
for (int odd = 0; odd <= 1; ++odd) {
ntt(G[v][odd], len, 1);
for (int j = 0; j <= sz - Siz[v]; ++j) ntt(F[u][odd][j], len, 1);
for (int j = 0; j <= Siz[v]; ++j) ntt(F[v][odd][j], len, 1);
for (int j = 0; j <= sz; ++j) ntt(f[odd][j], len, 1);
}
for (int odd = 0; odd <= 1; ++odd)
for (int j = 0; j <= sz; ++j)
for (int k = 0; k <= sz * (sz + 1) / 2; ++k)
F[u][odd][j][k] = f[odd][j][k];
}
calcG(u);
}
void solve() {
dfs1(1);
dfs2(1);
int ans = 0;
for (int odd = 0; odd <= 1; ++odd)
for (int k = 0; k < N * (N + 1) / 2; ++k) {
if (odd == 1) inc(ans, mul(G[1][odd][k], inv(N * (N + 1) / 2 - k)));
else dec(ans, mul(G[1][odd][k], inv(N * (N + 1) / 2 - k)));
}
ans = mul(ans, N * (N + 1) / 2);
printf("%d\n", ans);
}
void clear() {
memset(Fa, 0, sizeof Fa);
memset(Siz, 0, sizeof Siz);
memset(F, 0, sizeof F);
memset(G, 0, sizeof G);
Gr.clear();
}
int main() {
for (int i = 1; i <= 4096; i <<= 1) {
Rev[i][0] = 0;
for (int j = 1; j < i; ++j) {
Rev[i][j] = (Rev[i][j >> 1]) >> 1;
if (j & 1) Rev[i][j] |= (i >> 1);
}
Inv[i] = inv(i);
}
for (int f = 0; f <= 1; ++f)
for (int i = 1; i < 4096; i <<= 1)
W[f][i] = pw(pw(Prt, f == 0 ? 1 : Mod - 2), (Mod - 1) / (i << 1));
scanf("%d", &Te);
for (int t = 1; t <= Te; ++t) {
init();
printf("Case #%d: ", t);
solve();
clear();
}
return 0;
}