F - Colorful Tree(LCA,树上差分,离线处理)

F - Colorful Tree

给定一棵树,边有边权,且每条边有一个颜色,有 m m m次操作,

每次给定 x , y , u , v x, y, u, v x,y,u,v,如果把颜色为 x x x的边,边权修改为 y y y,求 u , v u, v u,v两点的距离,考虑

1 1 1号节点为根节点,

d [ i ] d[i] d[i] 1 1 1 i i i的距离, d i s ( u , v ) = d [ u ] + d [ v ] − 2 × d [ l c a ] dis(u, v) = d[u] + d[v] - 2 \times d[lca] dis(u,v)=d[u]+d[v]2×d[lca]

n u m [ i ] [ x ] num[i][x] num[i][x] 1 1 1 i i i,颜色为 x x x的数量, s u m [ i ] [ x ] sum[i][x] sum[i][x] 1 1 1 i i i,颜色为 x x x的边权和,

d ′ [ u ] = d [ u ] − s u m [ u ] [ x ] + n u m [ u ] [ x ] × y d'[u] = d[u] - sum[u][x] + num[u][x] \times y d[u]=d[u]sum[u][x]+num[u][x]×y d i s ′ ( u , v ) = d ′ [ u ] + d ′ [ v ] − 2 × d ′ [ l c a ] dis'(u, v) = d'[u] + d'[v] -2 \times d'[lca] dis(u,v)=d[u]+d[v]2×d[lca],离线处理一下即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

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

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

int n, m, num[N];

long long ans[N], sum[N], dis[N];

struct Res {
  int x, y, add, id;
};

vector<Res> a[N];

void add(int x, int y, int c, int d) {
  to[cnt] = y;
  nex[cnt] = head[x];
  col[cnt] = c;
  val[cnt] = d;
  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] + val[i];
    dfs1(to[i], rt);
    sz[rt] += sz[to[i]];
    if (!son[rt] || sz[son[rt]] < sz[to[i]]) {
      son[rt] = to[i];
    }
  }
}

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

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

void dfs(int rt, int fa) {
  for (auto &it : a[rt]) {
    ans[it.id] += 1ll * it.add * (dis[rt] - sum[it.x] + 1ll * num[it.x] * it.y);
  }
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == fa) {
      continue;
    }
    num[col[i]]++;
    sum[col[i]] += val[i];
    dfs(to[i], rt);
    num[col[i]]--;
    sum[col[i]] -= val[i];
  }
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d %d", &n, &m);
  for (int i = 1, x, y, c, d; i < n; i++) {
    scanf("%d %d %d %d", &x, &y, &c, &d);
    add(x, y, c, d);
    add(y, x, c, d);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  for (int i = 1, x, y, u, v; i <= m; i++) {
    scanf("%d %d %d %d", &x, &y, &u, &v);
    int f = lca(u, v);
    a[u].push_back({x, y, 1, i});
    a[v].push_back({x, y, 1, i});
    a[f].push_back({x, y, -2, i});
  }
  dfs(1, 0);
  for (int i = 1; i <= m; i++) {
    printf("%lld\n", ans[i]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值