JZOJ 4225. 【五校联考3day1】宝藏 (Standard IO)

题意

给出一棵\(n\)个点的树,一个点会等概率地向相邻的点走去,花费\(1\)的时间,现在给你若干个点\(v_0,v_1,...,v_p\),求\(v_0\)->\(v_1\)的期望时间+\(v_1\)->\(v_2\)的期望时间+\(v_{p-1}\)->\(v_p\)的期望时间。
\(n\leq 50000\),询问总点数\(\leq 50000\)

Solution

\(d_i\)表示\(i\)的度数,\(f_i\)表示\(i\)走向父亲的期望步数,转移两种情况:

  • 直接走向父亲
  • 走到某个儿子再走回来再走向父亲

得出这样的转移方程:
\(f_i=\frac{1}{d_i}+\frac{1}{d_i}\sum_{v\in son_i}1+f_v+f_i\)
经过简单的化简就能得到:
\(f_i=d_i+\sum_{v\in son_i}f_v\)
那么一个dfs就求出了\(f\)

\(g_i\)表示\(i\)从父亲走来的期望步数,转移三种情况:

  • 直接由父亲走来
  • 父亲走到另一个儿子再走回来再走到\(i\)
  • 父亲走到它的父亲再走回来再走到\(i\)

得出这样的转移方程:
\(g_i=\frac{1}{d_{fa_i}}+\frac{1}{d_{fa_i}}\sum_{v\in son_{fa_i},v\ne i}(1+f_v+g_i)+\frac{1}{d_{fa_i}}(1+g_{fa_i}+g_i)\)
经过简单的化简就能得到:
\(g_i=d_{fa_i}+g_{fa_i}+\sum_{v\in son_{fa_i},v\ne i}f_v\)
那么一个dfs就求出了\(g\)

在树上记录一下\(f\)\(g\)的前缀和(根到某个点的\(f\),\(g\)之和),然后加个倍增求lca,这题就顺利解决了。

Code

#include <cstdio>
#include <cstring>

typedef long long ll;
const int N = 50007;
void swap(int &a, int &b) { int t = a; a = b, b = t; }

int T;

int q, p, v[N];
int n, tot, st[N], to[N << 1], nx[N << 1], d[N], dep[N], anc[N][17];
ll ans, f[N], g[N], sumf[N], sumg[N];
void add(int u, int v) { to[++tot] = v, nx[tot] = st[u], st[u] = tot; }

void getf(int u, int from)
{
    ll sum = 0;
    for (int i = st[u]; i; i = nx[i]) if (to[i] != from) getf(to[i], u), sum += f[to[i]];
    f[u] = d[u] + sum;
}
void getg(int u, int from)
{
    ll sum = 0;
    for (int i = st[u]; i; i = nx[i]) if (to[i] != from) sum += f[to[i]];
    for (int i = st[u]; i; i = nx[i]) if (to[i] != from) g[to[i]] = g[u] + d[u] + sum - f[to[i]], getg(to[i], u);
}
void dfs(int u, int from)
{
    sumf[u] = sumf[from] + f[u], sumg[u] = sumg[from] + g[u];
    for (int i = st[u]; i; i = nx[i]) if (to[i] != from) dep[to[i]] = dep[u] + 1, anc[to[i]][0] = u, dfs(to[i], u);
}
int getlca(int u, int v)
{
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = 16; i >= 0; i--) if (dep[anc[u][i]] >= dep[v]) u = anc[u][i];
    if (u == v) return u;
    for (int i = 16; i >= 0; i--) if (anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i];
    return anc[u][0];
}
ll getlen(int u, int v)
{
    int lca = getlca(u, v);
    ll ret = sumf[u] - sumf[lca] + sumg[v] - sumg[lca];
    return ret;
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        tot = 0;
        memset(d, 0, sizeof(d)); memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g));
        memset(anc, 0, sizeof(anc)); memset(dep, 0, sizeof(dep)); memset(st, 0, sizeof(st)); memset(nx, 0, sizeof(nx));
        scanf("%d", &n);
        for (int i = 1, u, v; i < n; i++) scanf("%d%d", &u, &v), u++, v++, add(u, v), add(v, u), d[u]++, d[v]++;
        getf(1, 0), getg(1, 0), dep[1] = 1, dfs(1, 0);
        for (int j = 1; j <= 16; j++) for (int i = 1; i <= n; i++) anc[i][j] = anc[anc[i][j - 1]][j - 1];
        scanf("%d", &q);
        while (q--)
        {
            ans = 0;
            scanf("%d", &p);
            for (int i = 0; i <= p; i++) scanf("%d", &v[i]), v[i]++;
            for (int i = 1; i <= p; i++) ans += getlen(v[i - 1], v[i]);
            printf("%lld.0000\n", ans);
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/10328034.html
今日推荐