题意
略。
题解
看到这个(n leq 18),就知道可能是容斥。
结果是min-max
容斥。
原来要求一个点出发到一个点集最晚访问的点的期望时间,现在求的则是一个点出发到一个点集最早访问的点的期望时间。
这个东西可以树上dp,而且显然dp出来的东西是可以高斯消元的。
但是这样复杂度不对。
考虑有个树上高消的方法,建立在线性关系的基础上。
可以推一发式子:
[f_i = frac{1}{d_i} (f_{{fa}_i} + sum_{j in {son}_i} f_j) + 1
]
并设
[f_i = a_i f_{{fa}_i} + b_i
]
(就不推了,懒)
[f_i = frac{1}{d_i - sum_{j in {son}_i} a_j} f_{{fa}_i} + frac{sum_{j in {son}_i} b_j + d_i}{d_i - sum_{j in {son}_i} a_j}
]
然后就可以从下到上回溯的时候(mathcal O(n log p))计算了。
对于多组数据,其实可以直接预处理每种终点集合的答案,具体就是,终点集合里的每个点(i)的(a_i, b_i, f_i)都先为0,然后以出发点为根dp,同时计算(a)和(b)数组,则到这个子集的答案就是(f_{root})(即(b_{root})), 复杂度(mathcal O(2 ^ n n));
然后询问的时候使用min-max
容斥,枚举子集,每次(mathcal O(1))计算即可。
总复杂度(mathcal O(2 ^ n n + nq))。
#include <bits/stdc++.h>
using namespace std;
const int N = 19, mod = 998244353;
int n, q, rt, ans;
int a[N], b[N], d[N], fa[N];
int in[N], sgn[1 << N], mn[1 << N];
vector <int> g[N];
int power (int a, int b) {
int ret = 1;
for ( ; b; b >>= 1, a = 1ll * a * a % mod) {
if (b & 1) {
ret = 1ll * ret * a % mod;
}
}
return ret;
}
int inv (int x) {
return power(x, mod - 2);
}
void add (int x, int y) {
g[x].push_back(y), ++d[x];
}
void dfs (int x, int p) {
if (in[x]) {
a[x] = b[x] = 0;
return;
}
fa[x] = p, a[x] = b[x] = d[x];
for (auto y : g[x]) {
if (y != p) {
dfs(y, x);
a[x] = (a[x] - a[y] + mod) % mod;
b[x] = (b[x] + b[y]) % mod;
}
}
a[x] = inv(a[x]), b[x] = 1ll * b[x] * a[x] % mod;
}
int main () {
scanf("%d%d%d", &n, &q, &rt);
for (int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
for (int s = 1; s < (1 << n); ++s) {
sgn[s] = -1;
for (int i = 0; i < n; ++i) {
if (s >> i & 1) {
in[i + 1] = 1;
sgn[s] = -sgn[s];
} else {
in[i + 1] = 0;
}
}
dfs(rt, 0);
mn[s] = b[rt];
}
for (int k, x, s; q; --q) {
scanf("%d", &k), s = 0;
for (int i = 1; i <= k; ++i) {
scanf("%d", &x);
s |= 1 << (x - 1);
}
ans = 0;
for (int t = s; t; t = (t - 1) & s) {
ans = (ans + mn[t] * sgn[t]) % mod;
}
ans = (ans % mod + mod) % mod;
printf("%d
", ans);
}
return 0;
}