这里总结一下换根思路, 这里假设初始根为1, 换根后为$r$.
1, 单点修改
直接改即可, 不影响
2, 子树$x$修改或询问 (需要满足修改具有可减性)
(1)若x=r, 对全部节点加
(3)若x不在树链1->r上, 直接加即可
(3)若x在树链1->r上且x!=r, 全部节点加, 再对x在树链1->r方向上的儿子所在子树减
//calc(x,y)用来查询点x是否在树链1->y上
//若在的话返回x在树链1->y上的儿子
int calc(int x, int y) {
int f = x, pre = 0, lca;
while (top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
pre = top[x], x = fa[pre];
}
if (x==y) lca=x;
else lca=dep[x]<dep[y]?x:y;
if (lca!=f) return 0;
return fa[pre]==f?pre:son[f];
}
void update(int x, int v, int rt) {
if (x==rt) return _update(1,v);
int t = calc(x,rt);
if (!t) return _update(x,v);
_update(1,v),_update(t,-v);
}
int query(int x, int rt) {
if (x==rt) return _query(1);
int t = calc(x,rt);
if (!t) return _query(x);
return _query(1)-_query(t);
}
3, 查询lca(x,y), 假设根为1时的lca(x,y)为L
(1)若树链x->y与1->r无公共路径, 则为L
(2)若有公共路径(s,t), 则为s,t中深度较大的点
int lca(int u, int v, int rt) {
int L = lca(u,v);
if (_lca(rt,L)!=L) return L;
int x=_lca(u,rt),y=_lca(v,rt);
if (dep[x]<dep[y]) swap(x,y);
return x;
}