1.树链剖分的用处
对如下问题我们可以采用树链剖分的方法去做
1.把某个节点的子树的每个节点都加上一个值z
2.查询某个节点的子树的所有节点的值的和求出来
3.把一个节点x到y之间最短路径(经过边的条数最少)上的每个节点都加上某个值z
4.把一个节点x到y之间最短路径(经过边的条数最少)上的所有节点的和求出来
由于我们需要解决这些问题,所以我们要使用树链剖分这种算法。
2.实现原理:
1.知识储备:
重儿子:该节点的所有儿子中,子树中节点个数最多的儿子。
举例:节点A有两个儿子,G所形成的树中有6个结点分别为 G B E V J T {GBEVJT} GBEVJT,C所形成的树中有5个结点 C F D Z X {CFDZX} CFDZX。所以A的重儿子为G。对于j结点E,它的两个儿子的大小相同,于是重儿子可以是任何一个儿子。
2.操作
我们按照每一个结点的重儿子走就形成了一条重链,如图
我们要找出这个图上的所有重链,如图
这个书上就两条重链,你找不出第3个了。
然后我们按重链的顺序为结点打上时间戳。重链按顺序打,其余的按照dfs的顺序打。如图
我们定义这样几个数组完成重链剖分
int
fa[N],//记录该节点的父亲
dep[N],//记录该节点的深度
son[N],// 记录重儿子
siz[N],//记录该节点子树的大小(包含该节点)
top[N],//记录该重链的顶部
dfn[N],//时间戳
w[N],//
tim;//计数器
void dfs1(int u,int f)//处理fa,dep,siz,so
{
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;//当前节点的大小初始化为1(因为该节点本身算一个嘛)
int maxx=-1;
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].v;
if(v==f)
continue;//因为存的是无向图,所以一个边存两次,可能会回到该节点的父亲节点
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>maxx)
{
son[u]=v;
maxx=siz[v];
}
}
}
void dfs2(int u,int t)//处理dfn,top,w,t为该链的头
dfn[u]=++tim;
top[u]=t;
w[tim]=v[u];
if(!son[u])
return;
dfs2(son[u],t);
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].v;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
}
}
操作完成。解释一下为什么要这样做。
通过这样做我们强行把树的结点拆成一个连续的数组: A G E J T B V C F D X Z {AGEJTBVCFDXZ} AGEJTBVC