投放与花费

投放与花费

题目描述

给你一棵 n 个点的有边权的树,m 次操作,每次有参数 opt, x:

  • opt=1:在 x 点投放一个物品。
  • opt=2:查询点 x 到所有物品的距离和。

Solution

对于某个物品的投放点 u ,这次投放会对点 v 产生 \(dis_u + dis_v - 2\times dis_{lca(u,v)}\) 的贡献,其中 dis 表示根到该点边权和。

那么我们每次询问的答案就是 \(\sum_u dis_u + \sum_u dis_v +\sum_u dis_{lca(u,v)}\)

我们记 sum1 为目前为止投放点的 dis 和,即 \(sum1 = \sum_u dis_u\),sum2 为目前为止的投放次数,即 \(sum2 = \sum_u1\)

某次查询 v 答案即为 \(sum1 +sum2 \times dis_v - 2 \times \sum_u dis_{lca(u,v)}\),现在问题就是如何快速计算 \(\sum_u dis_{lca(u,v)}\)

其实这个问题单独有个题,可以参考 [LNOI2014]LCA

做法就是对每个投放点 u,将它到根的路径贡献 +1。查询 v 时查询 v 到根贡献和即为 \(dis_{lca(u,v)}\),如图:

a 即为 lca(u,v)。橙色点即为有贡献的点,我们现在对 v 到根求贡献和,就可以求出 \(dis_a\)

事情简单了起来,树剖或什么东西维护路径操作,简单查询,答案为

\[ans_v = sum1 + sum2 \times dis_v - 2 \times Ask(v,1) \]

注意:带权图中线段树要稍微加个权,如下代码中的 t[p].sum。

\(\mathbf{Code:}\)

#include <bits/stdc++.h>
#define int long long
#define swap(x, y)   ((x) ^= (y) ^= (x) ^= (y))
#define Rep(i, a, b) for (int i = (a), bb = (b); i <= bb; ++i)
#define S_H(T, i, u) for (int i = T.fl[u], v; v = T.to[i], i; i = T.net[i])
const int N = 2e5 + 10;
using namespace std;
int n, m;
int a[N], top[N], si[N], d[N], Fa[N], hs[N], pr[N], tr[N], dis[N], vs = 0;
struct Tree { int to[N << 1], net[N << 1], w[N << 1], fl[N], len;
    inline void inc(int x, int y, int z) { to[++len]=y,net[len]=fl[x],w[len]=z,fl[x]=len; }
} T;
struct Sep { int sum, la, val; };
struct Segmentree { Sep t[N << 2];
    #define ls  (p << 1)
    #define rs  (ls | 1)
    #define mid ((x + y) >> 1)
    #define Ls  ls, x, mid
    #define Rs  rs, mid + 1, y
    inline void Push(int p) { t[p].val = t[ls].val + t[rs].val; }
    inline void Build(int p, int x, int y) { t[p].la = 0;
        if (x == y) return t[p].sum = pr[x], void();
        return Build(Ls), Build(Rs), t[p].sum = t[ls].sum + t[rs].sum, void();
    }
    inline void Update(int p) {
        t[ls].val += t[ls].sum * t[p].la, t[rs].val += t[rs].sum * t[p].la,
        t[ls].la += t[p].la, t[rs].la += t[p].la, t[p].la = 0;
    }
    inline void Modify(int p, int x, int y, int l, int r, int v) {
        if (l > r || x > y || l > y || x > r) return void();
        if (l <= x && y <= r) return t[p].val += t[p].sum * v, t[p].la += v, void();
        Update(p);
        return Modify(Ls, l, r, v), Modify(Rs, l, r, v), Push(p);
    }
    inline int Ask(int p, int x, int y, int l, int r) {
        if (l > r || x > y || x > r || l > y) return 0;
        if (l <= x && y <= r) return t[p].val;
        Update(p);
        return Ask(Ls, l, r) + Ask(Rs, l, r);
    }
} Se; // definitions

template <class I> inline void read(I &s) {
    char c = getchar(); for (; !isdigit(c); c = getchar());
    for (s = 0; isdigit(c); c = getchar()) s = (s << 3) + (s << 1) + c - 48;
}
template <class I> inline void write(I x) {
    (x < 0 ? x = ~x + 1, putchar('-') : 0), (x > 9 ? write(x / 10) : void());
    return putchar(x % 10 + 48), void();
} // fast io

void Dfs(int u, int fa) { si[u] = 1, d[u] = d[fa] + 1, Fa[u] = fa;
    S_H(T, i, u) { if (v == fa) continue;
        dis[v] = dis[u] + T.w[i], a[v] = T.w[i], Dfs(v, u), si[u] += si[v], (si[hs[u]] < si[v] ? hs[u] = v : 0);
    }
}
void Dfs_chain(int u, int k) {
    top[tr[u] = ++vs, u] = k, pr[vs] = a[u]; if (!hs[u]) return void(); Dfs_chain(hs[u], k);
    S_H(T, i, u) { if (v == Fa[u] || v == hs[u]) continue; Dfs_chain(v, v); }
}
inline int Lca(int x, int y) {
    while (top[x] ^ top[y]) (d[top[x]] < d[top[y]] ? swap(x, y) : 0), x = Fa[top[x]]; return d[x] < d[y] ? x : y;
}
inline void Modify(int x) { while (x) Se.Modify(1, 1, n, tr[top[x]], tr[x], 1), x = Fa[top[x]]; }
inline int Ask(int x) { int sum = 0; while (x) sum += Se.Ask(1, 1, n, tr[top[x]], tr[x]), x = Fa[top[x]]; return sum; }
// Tree chain subdivision

signed main(void) {
    freopen("express.in", "r", stdin), freopen("express.out", "w", stdout);
    read(n), read(m);
    Rep(i, 1, n - 1) { int x, y, z; read(x), read(y), read(z), T.inc(x, y, z), T.inc(y, x, z); }
    Dfs(1, 0), Dfs_chain(1, 1), Se.Build(1, 1, n);
    int tmp1 = 0, tmp2 = 0;
    while (m --) {
        int opt, x; read(opt), read(x);
        if (opt == 1) Modify(x), tmp1 += dis[x], ++tmp2;
        else write(tmp1 + tmp2 * dis[x] - Ask(x) * 2), putchar(10);
    }
    return 0;
} // main

posted @ 2020-10-07 11:13  云烟万象但过眼  阅读(79)  评论(0编辑  收藏  举报