题目传送门
sol:树剖解决,我们只维护每个节点和重儿子的边权,那么当一个节点权值改变时,也只需要修改该节点和其重儿子的边权,若该节点是其父亲节点的重儿子,则多修改一条该节点和其父亲节点的边权。查询操作时,若该节点不是父亲节点的重儿子,则暴力计算一次该节点和父亲节点的边权。
- 树链剖分
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 20010; inline int read() { int n = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); } while (c >= '0' && c <= '9') { n = 10 * n + (c ^ '0'); c = getchar(); } return f * n; } struct { int v, to; } edge[2 * MAXN]; int head[MAXN]; void add_edge(int u, int v) { static int tot = 0; edge[tot].v = v; edge[tot].to = head[u]; head[u] = tot ++; } int fat[MAXN], son[MAXN], siz[MAXN], val[MAXN]; void dfs1(int u, int f) { fat[u] = f, siz[u] = 1; for (int i = head[u]; i != -1; i = edge[i].to) { int v = edge[i].v; if (v == f) continue; dfs1(v, u); siz[u] += siz[v]; if (siz[v] > siz[son[u]]) son[u] = v; } } int gcd[MAXN], dep[MAXN], top[MAXN], dfn[MAXN]; void dfs2(int u, int t) { static int tot = 0; dfn[u] = ++ tot; dep[u] = dep[fat[u]] + 1; top[u] = t; if (t != u) gcd[dfn[u]] = __gcd(val[u], val[fat[u]]); if (son[u]) dfs2(son[u], t); for (int i = head[u]; i != -1; i = edge[i].to) { int v = edge[i].v; if (v == fat[u] || v == son[u]) continue; dfs2(v, v); } } int query(int u, int v, int k) { int ans = 0; while (top[u] != top[v]) { if (dep[top[u]] > dep[top[v]]) swap(u, v); for (int i = dfn[top[v]] + 1; i <= dfn[v]; i++) ans += gcd[i] <= k; ans += __gcd(val[top[v]], val[fat[top[v]]]) <= k; v = fat[top[v]]; } if (dep[u] > dep[v]) swap(u, v); for (int i = dfn[u] + 1; i <= dfn[v]; i++) ans += gcd[i] <= k; return ans; } int main() { int n = read(), m = read(); memset(head, -1, sizeof(head)); for (int i = 1; i <= n; i++) val[i] = read(); for (int i = 2; i <= n; i++) { int u = read(), v = read(); add_edge(u, v), add_edge(v, u); } dfs1(1, 0), dfs2(1, 1); for (int i = 1; i <= m; i++) { if (read() == 1) { int u = read(), k = read(); val[u] = k; if (top[u] != u) { gcd[dfn[u]] = __gcd(val[u], val[fat[u]]); } if (son[u]) { gcd[dfn[son[u]]] = __gcd(val[u], val[son[u]]); } } else { int u = read(), v = read(), k = read(); printf("%d ", query(u, v, k)); } } return 0; }
------------------------------------------------------------分隔线------------------------------------------------------------
总结:$son,siz$必须在$dfs1$里求出;$dfn,top$必须在$dfs2$里求出;这次的错误是把$dfn$写到$dfs1$里面了,但是两次深搜的顺序是不一样的。第一次深搜就是按边的输入顺序进行搜索,第二次深搜在此基础上还优先搜索了重儿子。也正是因为优先搜索重儿子,才能保证重链的$dfs$序是连续的。