树链剖分详解

· · 个人记录

食用之前请务必搞清楚线段树

什么是树链剖分

轻重链剖分

引入几个概念:

举个例子来详细说一下。

树链剖分的好处

第一个dfs代码:

inline dfs1(int u, int f)//两个参数:u是现在的点,f是u的父亲
{
    size[u] = 1;//最开始u的子树中只有u一个点
    for(edge *p = head[u]; p; p = p->next)//遍历每一个与u相连的点
    {
        int v = p->v;
        if(v == f) continue;//如果此点是u的父亲就跳过
        dep[v] = dep[u] + 1;
        fa[v] = u;//处理信息
        dfs1(v, u);//继续递归
        size[u] += size[v];//u的子树大小要加上v的子树大小
        if(size[wson[u]] < size[v]) wson[u] = v;//更新重儿子
    }
}

代码:

inline void dfs2(int u, int tp)//u是当前点,tp是该链链首
{
    dfn[u] = ++tot, pre[tot] = u, top[u] = tp;
    //pre是dfn的反函数。如dfn[2] = 3,pre[3] = 2...
    //为什么要有这个反函数,因为在建线段树的时候是用dfn建的,然而如果想要知道原来那个点的信息就要知道pre
    if(wson[u]) dfs2(wson[u], tp);//有重子就继续往下拉重链
    for(edge * p = head[u]; p; p = p->next)//回过头来在轻儿子中拉链
    {
        int v = p->v;
        if(v != fa[u] && v != wson[u]) dfs2(v, v);//如果不是爸爸或重儿子,已该点为链首继续拉链
    }
}

至此,轻重链剖分完成,然后便是第二大块——

线段树维护重链

inline void build(node *r, int left, int right)
{
    r->left = left, r->right = right, r->lazy = -1, r->s = 0;
    if(left == right)
    {
         r->s = value[pre[left]];
         //这里是pre[left]不是left!原因就是刚才说的:这里所有的参数都是以dfn的形象出现的,而不是原来那个点的序号。
         return ;
    }
    int mid = (left + right) / 2;
    node *lson = &pool[++cnt], *rson = &pool[++cnt];
    r->ch[0] = lson, r->ch[1] = rson;
    build(lson, left, mid); build(rson, mid + 1, right);
    pushup(r);
}

基础的查询修改操作(在重链上的)就是原来的函数,怎么写详细请看线段树基础。

最后一步,也就是——

两点间路径的修改&查询

上图

Q1:如果想要求11与2之间的和该怎样? A1:在同一重链上,直接调用query查询就行了。

Q2:求6与7之间的和? A2:将6跳到1,同时答案加上query(dfn[2],dfn[6]);然后将7跳到1,答案加上query(dfn[3],dfn[7])

代码(修改的):

inline void modify(int u, int v, int d)
{
    while(top[u] != top[v]) //直到跳到一条链上
    {
        if(dfn[top[u]] < dfn[top[v]]) swap(u, v);//这里根据链首的值交换一下
        change(root, dfn[top[u]], dfn[u], d), u = fa[top[u]];//最后u=链首的爸爸
    }
    if(dep[u] > dep[v]) swap(u, v);
    change(root, dfn[u], dfn[v], d);
    //Q1
}

查询的(基本类似):

inline int Qsum(int u, int v)
{
    int ret = 0;
    while(top[u] != top[v]) 
    {
        if(dfn[top[u]] < dfn[top[v]]) swap(u, v);
        ret += query(root, dfn[top[u]], dfn[u]), u = fa[top[u]];
    }
    if(dep[u] > dep[v]) swap(u, v);
    ret += query(root, dfn[u], dfn[v]);
    return ret;
}

至此基本完结

类模板题

CF343D,[NOI2015]软件包管理器,[SHOI2012]魔法树,[SDOI2011]染色,[ZJOI2008]树的统计