小V和gcd树

树上问题,每次询问与两点有关,可以确定应该使用树链剖分来求解了。

这个问题中涉及到边的查询,树链剖分中有一个很常用的操作就是边权下放到点权上,

但这个操作一般是对有根树而言的,当然我们虚拟号节点为跟节点也是一样的。

我们假设这棵树的根节点为号节点,然后把所有的边权下放到其对应的节点上(1号节点所代表的边权就为空了)。

对于查询操作:查找,用树链剖分在最短路上跳转遍历所有的边,然后统计答案。

对于修改操作:查找,对于每个节点,我们需要修改其与父节点的权值,其与儿子节点的权值。

但是这样只能过的测试点,所以考虑如何优化。

查询操作是无法优化了,我们只能考虑如何简化修改操作毕竟是无法通过这题的。

大致思路跟原来还是一样的,只是我们要减小查询操作的常数。

我们约定,是节点的父节点,当且仅当的时候(作为的重儿子),我们才下放边权上。

所以对于修改操作我们只要修改两个点了:

  • 当这个点不是某条链的时,修改其与其父节点的值。
  • 当这个点有重儿子时,修改其与其重儿子的值。

对于查询操作我们也要做适当的调整,在之前我们的查询操作是从的值进行查询,

但是因为没有记录,所以我们的查询应该是从进行查询,

然后单独算

这样我们可以保证查询复杂度不会变差同样也是的,并且保证了修改操作时的。

#include <bits/stdc++.h>

using namespace std;

const int N = 2e4 + 10;

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

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

int n, m, a[N], value[N];

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

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

void dfs2(int rt, int tp) {
  rk[++tot] = rt, id[rt] = tot, top[rt] = tp;
  if (top[rt] != rt) {
    a[id[rt]] = __gcd(value[rt], value[fa[rt]]);
  }
  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 query(int x, int y, int k) {
  int ans = 0;
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) {
      swap(x, y);
    }
    for (int i = id[top[x]] + 1; i <= id[x]; i++) {
      ans += a[i] <= k;
    }
    if (fa[top[x]]) {
      ans += __gcd(value[top[x]], value[fa[top[x]]]) <= k;
    }
    x = fa[top[x]];
  }
  if (dep[x] > dep[y]) {
    swap(x, y);
  }
  for (int i = id[x] + 1; i <= id[y]; i++) {
    ans += a[i] <= k;
  }
  return ans;
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &value[i]);
  }
  for (int i = 1; i < n; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    add(x, y);
    add(y, x);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  while (m--) {
    int op, u, v, x, k;
    scanf("%d %d", &op, &u);
    if (op & 1) {
      scanf("%d", &x);
      value[u] = x;
      if (top[u] != u) {
        a[id[u]] = __gcd(value[fa[u]], value[u]);
      }
      if (son[u]) {
        a[id[son[u]]] = __gcd(value[u], value[son[u]]);
      }
    }
    else {
      scanf("%d %d", &v, &k);
      printf("%d\n", query(u, v, k));
    }
  }
  return 0;
}