#6073. 「2017 山东一轮集训 Day5」距离(树链剖分 + 永久标记主席树)

#6073. 「2017 山东一轮集训 Day5」距离

给定一颗有 n n n个节点带边权的树,以及一个排列 p p p p a t h ( u , v ) path(u, v) path(u,v) u , v u, v u,v路径上的点集, d i s t ( u , v ) dist(u, v) dist(u,v) u , v u, v u,v之间的最短路的长度。

m m m次询问,每次给定 u , v , k u, v, k u,v,k,要求 ∑ i ∈ p a t h ( u , v ) d i s t ( p i , k ) \sum\limits_{i \in path(u, v)}dist(p_i, k) ipath(u,v)dist(pi,k),要求在线求解。

我们选定 1 1 1号节点为根节点,定义 d ( i ) d(i) d(i)为点 i i i到根节点的距离,考虑初始的 ∑ i ∈ p a h t ( u , v ) d i s t ( p i , k ) \sum\limits_{i \in paht(u, v)} dist(p_i, k) ipaht(u,v)dist(pi,k),有如下:
∑ i ∈ p a t h ( u , v ) d i s t ( p i , k ) = ∑ i ∈ p a t h ( u , v ) ( d ( p i ) + d ( k ) − 2 × d ( l c a ( p i , k ) ) ) \sum\limits_{i \in path(u, v)}dist(p_i, k) = \sum_{i \in path(u, v)}\left(d(p_i) + d(k) - 2 \times d(lca(p_i, k))\right)\\ ipath(u,v)dist(pi,k)=ipath(u,v)(d(pi)+d(k)2×d(lca(pi,k)))
对于 d ( k ) d(k) d(k)的计算,由于 d ( k ) d(k) d(k)是一个定值,则这一部分的答案即为 u , v u, v u,v间点的个数$ \times d(k)$。

对于 d ( p i ) d(p_i) d(pi)的计算,我们考虑树上差分前缀和来求解,我们定义 s u m [ n ] = ∑ i ∈ p a h t ( 1 , n ) d ( p i ) sum[n] = \sum\limits_{i \in paht(1, n)}d(p_i) sum[n]=ipaht(1,n)d(pi)

∑ i ∈ p a t h ( u , v ) d ( p i ) = s u m [ u ] + s u m [ v ] − s u m [ l c a ( u , v ) ] − s u m [ f a ( l c a ( u , v ) ) ] \sum\limits_{i \in path(u, v)} d(p_i) = sum[u] + sum[v] - sum[lca(u, v)] - sum[fa(lca(u, v))] ipath(u,v)d(pi)=sum[u]+sum[v]sum[lca(u,v)]sum[fa(lca(u,v))]

最后一步,考虑最难算的 ∑ i ∈ p a t h ( u , v ) d ( l c a ( p i , k ) ) \sum\limits_{i \in path(u, v)} d(lca(p_i, k)) ipath(u,v)d(lca(pi,k)),可以仿照P4211 [LNOI2014]LCA这题的计算方式,

由于强制在线,所以这题必须用主席树,我们定义点 i i i所代表的主席树为从 1 − > i 1->i 1>i上,也就是根节点到 i i i上,

p i − > 1 p_i->1 pi>1所代表的信息,最后我们只需要四颗主席树即可解决问题 u + v − l c a ( u , v ) − f a ( l c a ( u , v ) ) u + v - lca(u, v) - fa(lca(u, v)) u+vlca(u,v)fa(lca(u,v))

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;

int head[N], to[N << 1], nex[N << 1], value[N << 1], cnt = 1;

int p[N], n, m, type;

int fa[N], sz[N], son[N], top[N], rk[N], id[N], dep[N], w[N], tot;

int root[N], ls[N * 100], rs[N * 100], num;

ll dis[N], len[N], s[N], sum[N * 100], lazy[N * 100];

void add(int x, int y, int w) {
  to[cnt] = y;
  nex[cnt] = head[x];
  value[cnt] = w;
  head[x] = cnt++;
}

void dfs1(int rt, int f) {
  fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1;
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == f) {
      continue;
    }
    dis[to[i]] = dis[rt] + value[i];
    dfs1(to[i], rt);
    w[to[i]] = value[i];
    sz[rt] += sz[to[i]];
    if (!son[rt] || sz[son[rt]] < sz[to[i]]) {
      son[rt] = to[i];
    }
  }
}

void dfs2(int rt, int tp) {
  rk[++tot] = rt, id[rt] = tot, len[tot] = w[rt], top[rt] = tp;
  if (!son[rt]) {
    return ;
  }
  dfs2(son[rt], tp);
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == fa[rt] || to[i] == son[rt]) {
      continue;
    }
    dfs2(to[i], to[i]);
  }
}

int lca(int u, int v) {
  while (top[u] != top[v]) {
    if (dep[top[u]] < dep[top[v]]) {
      swap(u, v);
    }
    u = fa[top[u]];
  }
  return dep[u] < dep[v] ? u : v;
}

void update(int &rt, int pre, int l, int r, int L, int R) {
  rt = ++num;
  ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre], lazy[rt] = lazy[pre];
  sum[rt] += len[min(r, R)] - len[max(l, L) - 1];
  if (l >= L && r <= R) {
    lazy[rt] += 1;
    return ;
  }
  int mid = l + r >> 1;
  if (L <= mid) {
    update(ls[rt], ls[pre], l, mid, L, R);
  }
  if (R > mid) {
    update(rs[rt], rs[pre], mid + 1, r, L, R);
  }
}

ll query(int u, int v, int f, int ff, int l, int r, int L, int R) {
  if (l >= L && r <= R) {
    return sum[u] + sum[v] - sum[f] - sum[ff];
  }
  ll ans = (lazy[u] + lazy[v] - lazy[f] - lazy[ff]) * (len[min(r, R)] - len[max(l, L) - 1]);
  int mid = l + r >> 1;
  if (L <= mid) {
    ans += query(ls[u], ls[v], ls[f], ls[ff], l, mid, L, R);
  }
  if (R > mid) {
    ans += query(rs[u], rs[v], rs[f], rs[ff], mid + 1, r, L, R);
  }
  return ans;
}

void dfs(int rt, int f) {
  s[rt] = s[f] + dis[p[rt]];
  int cur = p[rt];
  root[rt] = root[f];
  while (cur) {
    update(root[rt], root[rt], 1, n, id[top[cur]], id[cur]);
    cur = fa[top[cur]];
  }
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == f) {
      continue;
    }
    dfs(to[i], rt);
  }
}

ll query(int u, int v, int f, int ff, int rt) {
  ll ans = 0;
  while (rt) {
    ans += query(root[u], root[v], root[f], root[ff], 1, n, id[top[rt]], id[rt]);
    rt = fa[top[rt]];
  }
  return ans;
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d %d %d", &type, &n, &m);
  for (int i = 1, u, v, w; i < n; i++) {
    scanf("%d %d %d", &u, &v, &w);
    add(u, v, w);
    add(v, u, w);
  }
  for (int i = 1; i <= n; i++) {
    scanf("%d", &p[i]);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  for (int i = 1; i <= n; i++) {
    len[i] += len[i - 1];
  }
  dfs(1, 0);
  ll ans = 0;
  for (int i = 1, u, v, k; i <= m; i++) {
    scanf("%d %d %d", &u, &v, &k);
    u = u ^ (ans * type), v = v ^ (ans * type), k = k ^ (ans * type);
    int f = lca(u, v), ff = fa[f];
    ans = (dep[u] + dep[v] - dep[f] - dep[ff]) * dis[k];
    ans += s[u] + s[v] - s[f] - s[ff];
    ans -= 2 * query(u, v, f, ff, k);
    printf("%lld\n", ans);
  }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值