#6073. 「2017 山东一轮集训 Day5」距离
分析:
询问$\sum\limits_{i \in path(u,v)} dis(p_i, k)$,将前面的路径转化一下,分别求$path(u,root), path(v,root), path(lca, root), path(lca, fa[root])$的答案。
于是现在问题转化为求$\sum\limits_{i \in path(x,root)} dis(p_i, k)$,我们可以对每个$dis(p_i, k)$分成三部分,$dis(p_i, root) + dis(k,root) -2 \times dis(lca(p_i, k), root)$。
第一部分可以dfs一个前缀和维护,第二部分是$dep[x] \times dis(k, root)$,那么现在主要的就是求第三部分。
求一个点与其他所有点的lca的深度,可以看这道题LNOI2014 LCA,而这里不是深度了,是到根的距离,同样为每个点增加一个点权,为这个点上面那条边的边权,同时用树剖线段树维护每个点出现的次数,于是可以用相同的方法做了。
对每个每个点,都建一棵线段树空间太大,于是可以和可持久化线段树的做法一样,每次只加入修改的线段。
代码实现上有许多技巧,详见代码。
代码:
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> #include<bitset> #define fore(i, u, v) for (int i = head[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to) using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 200005; struct Edge { int to, nxt, w; } e[N << 1]; int head[N], siz[N], son[N], dep[N], bel[N], xl[N], pos[N], fa[N], val[N], ls[N * 100], rs[N * 100], p[N], Root[N]; int En, TreeIndex, Index, n, NowId; LL tsum[N << 2], sum[N * 100], add[N * 100], dis[N], dissum[N]; inline void add_edge(int u,int v,int w) { ++En; e[En] = (Edge){v, head[u], w}; head[u] = En; ++En; e[En] = (Edge){u, head[v], w}; head[v] = En; } void dfs1(int u) { dep[u] = dep[fa[u]] +1; siz[u] = 1; fore(i, u, v) if (v != fa[u]) { fa[v] = u, val[v] = e[i].w, dis[v] = dis[u] + e[i].w, dfs1(v); siz[u] += siz[v]; son[u] = siz[v] > siz[son[u]] ? v : son[u]; } } void dfs2(int u,int top) { bel[u] = top, pos[u] = ++Index; xl[Index] = u; if (!son[u]) return ; dfs2(son[u], top); fore(i, u, v) if (v != fa[u] && v != son[u]) dfs2(v, v); } int LCA(int u,int v) { while (bel[u] != bel[v]) dep[bel[u]] > dep[bel[v]] ? u = fa[bel[u]] : v = fa[bel[v]]; return dep[u] < dep[v] ? u : v; } void build(int l,int r,int rt) { if (l == r) { tsum[rt] = val[xl[l]]; return ; } int mid = (l + r) >> 1; build(l, mid, rt << 1); build(mid + 1, r, rt << 1 | 1); tsum[rt] = tsum[rt << 1] + tsum[rt << 1 | 1]; } void Insert(int l,int r,int rt,int &now,int pre,int L,int R) { if (now <= NowId) now = ++TreeIndex, sum[now] = sum[pre], add[now] = add[pre], ls[now] = ls[pre], rs[now] = rs[pre]; if (L <= l && r <= R) { add[now] ++; sum[now] = tsum[rt] * add[now]; if (ls[now]) sum[now] += sum[ls[now]]; if (rs[now]) sum[now] += sum[rs[now]]; return ; } int mid = (l + r) >> 1; if (L <= mid) Insert(l, mid, rt << 1, ls[now], ls[pre], L, R); if (R > mid) Insert(mid + 1, r, rt << 1 | 1, rs[now], rs[pre], L, R); sum[now] = tsum[rt] * add[now]; if (ls[now]) sum[now] += sum[ls[now]]; if (rs[now]) sum[now] += sum[rs[now]]; } LL query(int l,int r,int rt,int now,int L,int R,LL v) { if (L <= l && r <= R) return sum[now] + tsum[rt] * v; int mid = (l + r) >> 1; LL res = 0; if (L <= mid) res = query(l, mid, rt << 1, ls[now], L, R, v + add[now]); if (R > mid) res += query(mid + 1, r, rt << 1 | 1, rs[now], L, R, v + add[now]); return res; } void update(int x,int y) { NowId = TreeIndex; int t = x; x = p[x]; while (x) { Insert(1, n, 1, Root[t], Root[y], pos[bel[x]], pos[x]); x = fa[bel[x]]; } } void solve(int u) { dissum[u] = dissum[fa[u]] + dis[p[u]]; update(u, fa[u]); fore (i, u, v) if (v != fa[u]) solve(v); } LL Calc(int x,int k) { LL ans = 0; while (k) { ans += query(1, n, 1, Root[x], pos[bel[k]], pos[k], 0); k = fa[bel[k]]; } return ans; } LL getans(int x,int k) { return dissum[x] + dis[k] * dep[x] - 2 * Calc(x, k); } int main() { int ty = read();n = read();int m = read(); for (int u, v, w, i = 1; i < n; ++i) u = read(), v = read(), w = read(), add_edge(u, v, w); for (int i = 1; i <= n; ++i) p[i] = read(); dfs1(1); dfs2(1, 1); build(1, n, 1); solve(1); LL x, y, k, lastans = 0 ; while (m --) { x = read(), y = read(), k = read(); x ^= (ty * lastans), y ^= (ty * lastans), k ^= (ty * lastans); int z = LCA(x, y); lastans = getans(x, k) + getans(y, k) - getans(z, k) - getans(fa[z], k); cout << lastans << "\n"; } return 0; }