【JZOJ 5814】【NOIP提高A组模拟2018.8.14】 树
题目大意:
给定 \(Q\) 个询问,每个询问给出 \(u,v\),求从 \(u\) 走到 \(v\) 的期望步数。
正文:
设 \(f_i\) 表示第 \(i\) 个点到其父节点的期望步数,\(g_i\) 表示其父节点到第 \(i\) 个点的期望步数。则有:
\[f_x=\frac{1}{deg_x}+\sum_{y\in son(x)}\frac{1+f_x+f_y}{deg_x} \\ g_x=\frac{1}{deg_{fa(x)}}+\frac{1+g_x+g_{fa(x)}}{deg_{fa(x)}}+\sum_{y\in son(fa(x))|y\ne x}\frac{g_x+1+f_y}{deg_{fa(x)}}
\]
(\(deg_x\) 表示节点 \(x\) 的度)
化简这两个式子:
\[\begin{aligned}f_x\cdot deg_x &=1+\sum_{y\in son(x)}f_x+1+f_y\\f_x\cdot deg_x&=dge_x+(deg_x-1)f_x+\sum_{y\in son(x)}f_y\\ f_x&=deg_x+\sum_{y\in son(x)}f_y\end{aligned}
\]
\[\begin{aligned}g_x\cdot deg_{fa(x)}&=2+g_x+g_{fa(x)}+\sum_{y\in son(fa(x))|y\ne x}f_y\\ g_x\cdot deg_{fa(x)}&=deg_{fa(x}+(deg_{fa(x)}-1)g_x+g_{fa(x})+\sum_{y\in son(fa(x))|y\ne x}f_y\\g_x&=deg_{fa(x)}+g_{fa(x)}+\sum_{y\in son(fa(x))|y\ne x}f_y\end{aligned}
\]
用树形DP求出 \(f,g\),用LCA求出距离。
代码:
void dfs (int u, int fa)
{
for (int i = head[u]; i; i = next[i])
deg[u]++;
ff[u] = deg[u];
for (int i = head[u]; i; i = next[i])
{
int v = to[i];
if (v == fa) continue;
dfs(v, u);
ff[u] += ff[v];
ff[u] %= mod;
}
}
void dfs1 (int u, int fa)
{
int sum = deg[u];
for (int i = head[u]; i; i = next[i])
{
int v = to[i];
if(v == fa) {sum += g[u];sum%=mod;continue;}
sum += ff[v];
sum %= mod;
}
for (int i = head[u]; i; i = next[i])
{
int v = to[i];
if(v == fa) continue;
g[v] = ((sum - ff[v]) % mod + mod) % mod;
dfs1(v, u);
}
}
void dfs2 (int u, int fa)
{
d[u] = d[fa] + 1;
f[u][0] = fa;
for (int i = head[u]; i; i = next[i])
{
int v = to[i];
if(v == fa) continue;
ff[v] += ff[u];
ff[v] %= mod;
g[v] += g[u];
g[v] %= mod;
dfs2 (v, u);
}
}
int lca (int x, int y)
{
if (d[x] > d[y])
{
int t = x;
x = y;
y = t;
}
for (int i = num; i >= 0; i--)
if (d[f[y][i]] >= d[x])
y = f[y][i];
if (x == y) return x;
for (int i = num; i >= 0; i--)
if (f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
return f[x][0];
}
int main()
{
scanf ("%d%d", &n, &m);
num = (int) (log2(n)) + 1;
for (int i = 1; i < n; i++)
{
int u, v;
scanf ("%d%d", &u, &v);
Add(u, v), Add(v, u);
}
dfs(1, 0);
dfs1(1, 0);
dfs2(1, 0);
for (int i = 1; i <= 19; i++)
for (int j = 1; j <= n; j++)
f[j][i] = f[f[j][i - 1]][i - 1];
for (int i = 1; i <= m; i++)
{
int a, b;
scanf ("%d%d", &a, &b);
int LCA = lca(a, b);
printf("%d\n", ((ff[a] - ff[LCA] + g[b] - g[LCA]) % mod + mod) % mod);
}
return 0;
}