抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

LOJ 6073

题解

建议先做下这题的弱弱弱弱弱化版:LOJ #2558. 「LNOI2014」LCA

\(dis_i\) 表示根到 \(i\) 的路径上的边权之和,\(dep_i\) 表示根到 \(i\) 的路径上的点数,答案可以表示成

\[\begin{aligned}\sum_{i\in \text{path}(u,v)}\text{dist}(p_i,k)&=\sum_{i\in \text{path}(u,v)} \left(dis_{p_i}+dis_k-2dis_{\text{LCA}(p_i,k)}\right)\\&=\left(dep_u+dep_v-2dep_{\text{LCA}(u,v)}+1\right)dis_k\\&+\sum_{i\in \text{path}(u,v)} dis_{p_i}-2\sum_{i\in \text{path}(u,v)}dis_{\text{LCA}(p_i,k)}\end{aligned}\]

这个式子的第一项可以在预处理 \(dep_i,dis_i\) 后快速计算,第二项可以预处理根到 \(i\) 的路径上所有 \(dis_j\) 之和(记为 \(sumd_i\))然后快速计算,关键是最后一项,即计算

\[\sum_{i\in \text{path}(u,v)}dis_{\text{LCA}(p_i,k)}\]

的值。

联系 LOJ #2558. 「LNOI2014」LCA 的做法,我们有了一个初步的暴力做法:

我们设 \(w_i\) 表示 \(i\) 与它父亲的边的边权,对于 \(u\)\(v\) 路径上的每个点 \(i\),我们把 \(p_i\) 到根的路径上所有点 \(j\) 的“点权”加 \(w_j\),某个点的“点权”初始为 \(0\)。然后,我们查询根到 \(k\) 路径上所有点的“点权和”,这个值即为上面式子的值。

考虑优化这个暴力。与 LOJ #2558. 「LNOI2014」LCA 相似,我们可以差分。我们把 \(p_i\) 到根的路径上所有点 \(j\) 的“点权”加 \(w_j\) 这样的一次操作叫做对点 \(i\) 的一次操作。那么显然答案可以差分成对 \(u\) 到根路径上每个点进行一次操作后的 \(k\) 到根路径上的“点权”和,加上对 \(v\) 到根路径上每个点进行一次操作后的 \(k\) 到根路径上的“点权”和,减去对 \(\text{LCA}(u,v)\) 到根路径上每个点进行一次操作后的 \(k\) 到根路径上的“点权”和,减去对 \(\text{LCA}(u,v)\) 的父亲到根路径上每个点进行一次操作后的 \(k\) 到根路径上的“点权”和。注意上面四个操作都是在初始状态下进行的,并不是从上一个操作继承下来的。

四个操作我们可以对每个节点维护一棵线段树,第 \(i\) 个点的线段树维护对 \(i\) 到根路径上每个点进行一次操作后的区间的“点权”之和,注意这里的区间是指树剖后 DFS 序上的区间。求 \(k\) 到根路径上的点权和可以树链剖分后在线段树上 \(O(\log^2n)\) 得到。每个点的线段树可以从它父亲那里继承过来,即可持久化线段树,注意这里需要支持可持久化的区间修改、区间查询,线段树的写法与普通线段树有一些区别。

时间复杂度 \(O(n\log^2n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
long long read(){
register long long x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f ^= 1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 200005
int type, n, q, p[N];
int edge, to[N << 1], tw[N << 1], pr[N << 1], hd[N];
void addedge(int u, int v, int w){
to[++edge] = v, tw[edge] = w, pr[edge] = hd[u], hd[u] = edge;
}
long long dis[N], pre[N], sumd[N], ans;
int fa[N], sz[N], dep[N], son[N], top[N], idx, dfn[N], id[N];
void dfs(int u){
dep[u] = dep[fa[u]] + 1, sz[u] = 1;
for (register int i = hd[u], v; i; i = pr[i])
if ((v = to[i]) != fa[u])
fa[v] = u, dis[v] = dis[u] + tw[i], dfs(v), sz[u] += sz[v],
!son[u] || sz[v] > sz[son[u]] ? son[u] = v : 0;
}
void dfs(int u, int tp){
top[u] = tp, dfn[u] = ++idx, id[idx] = u, pre[idx] = dis[u] - dis[fa[u]];
if (son[u]) dfs(son[u], tp);
for (register int i = hd[u], v; i; i = pr[i])
if ((v = to[i]) != fa[u] && v != son[u]) dfs(v, v);
}
struct Chairman_Tree{
int cnt, ls[N * 150], rs[N * 150], lz[N * 150];
long long sum[N * 150];
void modify(int &u, int _u, int l, int r, int L, int R){
u = ++cnt, ls[u] = ls[_u], rs[u] = rs[_u], sum[u] = sum[_u], lz[u] = lz[_u];
if (L == l && r == R) return ++lz[u], void(0);
sum[u] += pre[R] - pre[L - 1];
int md = (l + r) >> 1;
if (R <= md) modify(ls[u], ls[_u], l, md, L, R);
else if (L > md) modify(rs[u], rs[_u], md + 1, r, L, R);
else modify(ls[u], ls[_u], l, md, L, md), modify(rs[u], rs[_u], md + 1, r, md + 1, R);
}
long long query(int u, int l, int r, int L, int R){
if (!u) return 0;
if (L == l && r == R) return sum[u] + 1ll * lz[u] * (pre[R] - pre[L - 1]);
long long tmp = 1ll * lz[u] * (pre[R] - pre[L - 1]);
int md = (l + r) >> 1;
if (R <= md) return tmp + query(ls[u], l, md, L, R);
else if (L > md) return tmp + query(rs[u], md + 1, r, L, R);
else return tmp + query(ls[u], l, md, L, md) + query(rs[u], md + 1, r, md + 1, R);
}
}T;
int rt[N];
void update(int u){
int _u = u;
u = p[u];
while (top[u] != 1){
T.modify(rt[_u], rt[_u], 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
T.modify(rt[_u], rt[_u], 1, n, 1, dfn[u]);
}
void build(int u){
rt[u] = rt[fa[u]], update(u), sumd[u] = sumd[fa[u]] + dis[p[u]];
for (register int i = hd[u], v; i; i = pr[i])
if ((v = to[i]) != fa[u]) build(v);
}
int lca(int u, int v){
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) std :: swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
long long query(int rt, int u){
long long res = 0;
while (top[u] != 1){
res += T.query(rt, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
res += T.query(rt, 1, n, 1, dfn[u]);
return res;
}
int main(){
type = read();
n = read(), q = read();
for (register int i = 1, u, v, w; i < n; ++i)
u = read(), v = read(), w = read(), addedge(u, v, w), addedge(v, u, w);
for (register int i = 1; i <= n; ++i) p[i] = read();
dfs(1), dfs(1, 1);
for (register int i = 1; i <= n; ++i) pre[i] += pre[i - 1];
build(1);
while (q--){
int u = read() ^ (type * ans), v = read() ^ (type * ans), k = read() ^ (type * ans);
int l = lca(u, v);
ans = sumd[u] + sumd[v] - sumd[l] - sumd[fa[l]];
ans += 1ll * (dep[u] + dep[v] - dep[l] - dep[fa[l]]) * dis[k];
ans -= 2 * (query(rt[u], k) + query(rt[v], k) - query(rt[l], k) - query(rt[fa[l]], k));
printf("%lld\n", ans);
}
}

评论