大概总结一下换根思路, 假设初始根为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; }