杀蚂蚁简单版

杀蚂蚁简单版

杀蚂蚁 (antbusters) 是一T3款风靡 Ol 界的游戏,下面介绍它的一个简单版本。游戏的地图是一棵 \(n\) 个节点的树, 其中 1 号点时蚂蚁窝, 并且 1 号点只与 2 号点直接相连。
现在有一只蚂蚁拿着蛋糕从树上某个节点 \(s\) 出发, 按照如下规则随机游走:
1.树上每一个节点\(i\) 都有一定数量的信息素 \(v_i\)
2.如果蚂蚁当前位于节点\(x\),与\(x\) 相邻的节点分别为\(y_1,y_2,\cdots,y_r\)。那么蚂蚁下一步走到节点\(y_i\quad(1\leq i\leq r)\) 的概率是
3.如果蚂蚁在某一步移动结束后位于 1 号点 (蚂蚁窝),游戏结束。
现在你有一个攻击装置,这个攻击装置的工作原理是:
1.游戏开始时,你需要选择树上的一条简单路径,这个路径就是该装置的攻击范围,在之后的游戏进程中你不可以改变它。
2.之后在每秒初,如果蚂蚁在这条路径上, 则蚂蚁就会被攻击而受到 \(1\) 点伤害。
你现在要进行 \(q\) 次游戏,每次游戏会给定蚂蚁出生点 \(s\) ,你想知道。如果在这次游戏中选择 \(x\) 号点到 \(y\) 号点这条简单路径作为攻击范围, 在游戏结束时,你对蚂蚁造成伤害的期望值是多少,由于答案非常大而且可能是个分数,你只需要输出答案对 998 244 353 取模的结果。
这里假设蚂蚁的血量是无限的,这意味着蚂蚁走到1 号点时游戏才会结束。

%%% EXODUS

先考虑进行一个较为难想的 DP,一般来说,这种游走类问题是将父亲作为一个阶段来考虑,因为父亲是唯一确定的

考虑设 \(f_u\)\(u\) 第一次到达其父亲期望经过的关键点的个数,设其附近的点的总权值和为 \(tot_u\),如果 \(u\) 为关键点,则 \(mrk_u=1\),则经过一番推导可以得到

\[f_u=\sum_{v\in son(u)} (\dfrac{val_v}{tot_u}(mrk_u+f_v+f_u)) + \dfrac{val_{fa_u}}{tot_u} mrk_u \]

其中求和部分表示从当前的节点到了一个儿子后再回来的期望,而后面则是从自己回到父亲的期望

一番导的推之后,会有以下结果

\[\dfrac{val_{fa_u}}{tot_u} f_u= \sum_{v\in son_u} (\dfrac{val_v}{tot_u}(mrk_u+f_v))+ \dfrac{val_{fa_u}}{tot_u} mrk_u \]

\[f_u= \sum_{v\in son_u} (\dfrac{val_v}{val_{fa_u}}(mrk_u+f_v))+mrk_u \]

这里就有一个可以计算的转移了,有了 \(O(nq)\) 的做法

不难发现一件事情,对于不同的 \(s,x,y\) ,只有\(mrk\) 是不同的,这启发我们对着路径上的关键点算出所有的值

这里再看一下这个转移柿子,这是一个只与子树有关的转移,考虑将自己的贡献以及子树对自己的贡献拆出来

\[f_u=\sum_{v\in son_u}(\dfrac{val_v}{val_{fa_u}}f_v) + \dfrac{tot_u}{val_{fa_u}} mrk_u \]

考虑一个点 \(u\) 对于其祖先 \(v\) 的贡献,不难发现是 \(\dfrac{tot_u}{val_{fa_u}}\times \dfrac{val_u}{val_{fa_{fa_u}}}\times\dots\),可以发现化简之后是\(\dfrac{tot_uval_u}{val_{fa_v}val_v}\)

考虑分开贡献这个东西,设 \(h1=lca(x,s),h2=lca(y,s),h=lca(x,y)\)

分两种情况讨论

  • \(h1=h2\)

不难发现这个时候从 \(x,y\)\(h1\) 的路径是等价的,求出这两条链上的 \(tot_uval_u\) 再乘上 \(h1\) 到根路径上的 \(\dfrac{1}{val_{fa_v}val_v}\) 就可以了

  • \(h1\ne h2\)

考虑设 \(h2=h\) ,那将链拆为三个部分,从 \(x\)\(h1\) 的部分可以给 \(h1\) 到根的部分做贡献,从 \(y\)\(h\) 的部分可以给 \(h\) 到根的部分做贡献,而从 \(h1\)\(h\) 的部分每一部分都可以给自己的祖先做贡献,分别维护前缀和,然后树上差分就可以了

复杂度瓶颈在 lca 和求逆元

const int N = 1e5 + 10;
int n, q;
int acc[20][N], dep[N], fa[N];
ll val[N], c[3][N], tot[N];
vector<int> g[N];

ll inv(ll x, ll p = mod - 2) {
    ll ans = 1;
    for (; p; p >>= 1) {
        if (p & 1)
            (ans *= x) %= mod;
        (x *= x) %= mod;
    }
    return ans;
}

void dfs(int now, int f) {
    acc[0][now] = f, dep[now] = dep[f] + 1;
    for (int u = acc[0][now], i = 0; u; u = acc[++i][now]) acc[i + 1][now] = acc[i][u];
    ll h1 = tot[now] * val[now] % mod, h2 = inv(val[now] * val[f] % mod);
    c[0][now] = (c[0][f] + h1) % mod;
    c[1][now] = (c[1][f] + h2) % mod;
    c[2][now] = (c[2][f] + h1 * c[1][now] % mod) % mod;
    for (int x : g[now])
        if (x ^ f)
            dfs(x, now);
}

int lca(int x, int y) {
    if (dep[x] < dep[y])
        swap(x, y);
    for (int gap = dep[x] - dep[y], bit = __lg(gap); gap; gap -= (1 << bit), bit = __lg(gap)) x = acc[bit][x];
    fd(i, __lg(dep[x]), 0) if (acc[i][x] ^ acc[i][y]) x = acc[i][x], y = acc[i][y];
    return (x ^ y) ? acc[0][x] : x;
}

ll calc(int x, int y, int d) {
    int z = lca(x, y);
    ll res = (c[d][x] + c[d][y]) % mod;
    res = (res + mod - c[d][z]) % mod;
    res = (res + mod - c[d][acc[0][z]]) % mod;
    return res;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    n = rd();
    fp(i, 1, n) val[i] = rd();
    fp(i, 1, n - 1) {
        int u = rd(), v = rd();
        (tot[u] += val[v]) %= mod;
        (tot[v] += val[u]) %= mod;
        g[u].emplace_back(v), g[v].emplace_back(u);
    }
    dfs(2, 1);
    q = rd();
    while (q--) {
        int s = rd(), x = rd(), y = rd();
        int h = lca(x, y), h1 = lca(x, s), h2 = lca(y, s);
        ll ans = 0;
        if (h1 == h2) {
            ans = calc(x, y, 0) * calc(h1, 2, 1) % mod;
        } else {
            if (h == h1)
                swap(x, y);
            h1 = lca(x, s);
            ans = (calc(x, h1, 0) + mod) % mod * calc(h1, 2, 1) % mod;
            (ans += (calc(y, h, 0) + mod - calc(h, h, 0)) % mod * calc(h, 2, 1) % mod) %= mod;
            (ans += calc(acc[0][h1], h, 2)) %= mod;
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2023-10-02 21:43  颈流推进  阅读(54)  评论(0)    收藏  举报