学习笔记--树链剖分

潇爷的讲解。。十分的nice,可惜人傻。。。然而YveH似乎很早就A过一道题。。。。现在开始学。。。

树链剖分就是兹辞把一棵树上的信息,切成多条链,再把这些链hash到某数据上,是之兹辞对原树上的信息进行快速的查询与修改。
把树上的路径分类为重链和轻链

PS此处为链剖线段树QwQ
定义:
记size[v]表示以v为根的子树的节点数,deep[v]表示v的深度(根深度为1),top[v]表示v所在的链的顶端节点,fa[v]表示v的父亲,son[v]表示与v在同一重链上的v的儿子节点(称为重儿子),w[v]表示v与其父亲节点的连边(称为v的父边)在线段树中的位置。只要把这些东西求出来,就能用logn的时间完成原问题中的操作。

**重儿子:**siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
轻儿子:v的其它子节点。
重边:点v与其重儿子的连边。
轻边:点v与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边。

剖分后的树有如下性质:
性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];
性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。

预处理:(dfs_1 && dfs_2)
dfs_1:
求出树上每个节点的深度deep[i],根的子树大小size[i],祖先的信息fa[i].以及son[i]之类的信息.
dfs_2:
以根为起点,向下拓展建重链,选择最大的子树继承,其余节点以该节点为起点重新做重链;
给每个点编号,每条重链相当于区间,用数据结构维护这区间,重链首尾相接,维护即可.
⒈对于v,当son[v]存在(即v不是叶子节点)时,显然有top[son[v]] = top[v]。线段树中,v的重边应当在v的父边的后面,记w[son[v]] = totw+1,totw表示最后加入的一条边在线段树中的位置。此时,为了使一条重链各边在线段树中连续分布,应当进行dfs_2(son[v]);
⒉对于v的各个轻儿子u,显然有top[u] = u,并且w[u] = totw+1,进行dfs_2过程。
这就求出了top和w。
将树中各边的权值在线段树中更新,建链和建线段树的过程就完成了。

具体代码:

void dfs_1(int now,int f,int d)
{
    deep[now]=d;fa[now]=f;num[now]=1;
    for (int i=end[now]; i; i=edge[i].next)
        {
            int go=edge[i].go;if (go==f) continue;
            dfs_1(go,now,d+1);num[now]+=num[go];
            if (!son[now] || num[go]>num[son[now]]) son[now]=go;
        }
}
void dfs_2(int now,int number)
{
    top[now]=number;tree[now]=++tot;
    pre[tree[now]]=now;
    if (!son[now]) return;
    dfs_2(son[now],number);
    for (int i=end[now]; i; i=edge[i].next)
        {
            int go=edge[i].go;
            if (go!=son[now] && go!=fa[now]) dfs_2(go,go);
        }
}

修改:
1.点修改 :根据编号在数据结构中修改
2.区间修改:(1)若L,R在一条重链上,直接修改loc[L]-loc[R]
(2)若L,R不在一条重链,一边进行修改,一边同时向上靠,靠的同一条重链上,变为情况(1)
查询操作同上…

链剖线段树模板题:(不稳定传送门)http://blog.csdn.net/dad3zz/article/details/50627897

posted @ 2016-02-03 11:06  DaD3zZ  阅读(155)  评论(0编辑  收藏  举报