投放与花费
投放与花费
题目描述
给你一棵 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