Description
给你一棵树,经过一条边的时间为(1)。有(q)次询问,每次询问给定一条有若干个关键点的路径,你需要依次经过这些关键点。你会从路径的起点开始,每次随机选择一条与当前点直接相连的边走过去。问你走完这条路径的期望时间为多少
Solution
首先,本题有这样一个性质(在树上)
对于任意一条链((x, y)),我们在链上任意取一个点(k),均满足(dis(x, k) + dis(k, y) = dis(x, y)),其中(dis(x, y))表示(x)点到(y)的期望时间
我们可以任选一个点作为根,然后预处理出两个数组(up[i], down[i]),分别表示(i)号点到它父亲需要的期望时间以及(i)号点父亲到它所需要的期望时间
然后我们就可以维护(up)和(down)的树上前缀和就可以求出任意一条路径的期望时间了
下面是一种比较简单的求出(up)和(down)的方法,(size[i])表示以(i)为根的子树大小
[up[i] = 2 cdot size[i] - 1 \
down[i] = 2 cdot (n - size[i]) - 1
]
还有一种比较容易理解,但理解比较麻烦的办法 传送门
Code
#include <bits/stdc++.h>
using namespace std;
#define fst first
#define snd second
#define mp make_pair
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
const int maxn = 5e4 + 10;
vector<int> g[maxn];
int n, m, d[maxn], sz[maxn], fa[maxn][16];
inline void dfs(int now, int f) {
sz[now] = 1, d[now] = d[f] + 1, fa[now][0] = f;
for (int i = 1; i <= 15; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
for (int i = 0; i < g[now].size(); i++) {
int son = g[now][i];
if (son == f) continue;
dfs(son, now);
sz[now] += sz[son];
}
}
LL up[maxn], dn[maxn];
inline void DFS(int now, int f) {
up[now] = (sz[now] << 1) - 1, dn[now] = ((n - sz[now]) << 1) - 1;
up[now] += up[f], dn[now] += dn[f];
for (int i = 0; i < g[now].size(); i++) {
int son = g[now][i];
if (son == f) continue;
DFS(son, now);
}
}
inline int lca(int x, int y) {
if (d[x] < d[y]) swap(x, y);
for (int i = 15; ~i; i--)
if (d[fa[x][i]] >= d[y]) x = fa[x][i];
if (x == y) return x;
for (int i = 15; ~i; i--)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
inline LL dis(int x, int y) {
int LCA = lca(x, y);
return up[x] - up[LCA] + dn[y] - dn[LCA];
}
int main() {
freopen("C.in", "r", stdin);
freopen("C.out", "w", stdout);
int T = read();
while (T--) {
n = read();
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i < n; i++) {
int x = read() + 1, y = read() + 1;
g[x].push_back(y), g[y].push_back(x);
}
dfs(1, 0), DFS(1, 0);
m = read();
for (int i = 1; i <= m; i++) {
LL ans = 0;
int cnt = read(), lst = read() + 1;
for (int j = 1; j <= cnt; j++) {
int now = read() + 1;
ans += dis(lst, now);
lst = now;
}
printf("%lld.0000
", ans);
}
if (T) printf("
");
}
return 0;
}