换根板子

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

 

原文地址:https://www.cnblogs.com/uid001/p/10633624.html