先简单介绍一下树链剖分;
树链剖分可以看成对树的预处理,对不同结构的树的所有节点用标号tid【i】表示,求解一个特殊标号tid【i】使得用这一组特殊标号对应到的相应的数据结构后对查询和修改的复杂度都优化为logn^2的级别的过程即可叫做对树的剖分。。一般的是用启发式数剖的方法求解tid【i】,即定义重儿子,轻儿子,重链,轻链相应概念,按照一遍dfs求出每个点的重儿子(没有儿子的默认为0),和每个点的深度dep,在求解这些信息后,再dfs一遍,并且先dfs重儿子,用每个重链的深度最小的点的tid【i】表示这个重链,同时因为每次都是按照重儿子的顺序dfs,所以重链上的编号都是连续的,即将重链为一个用两个信息维护的区间,对于轻儿子直接把它映射为单点,即重链分治,轻链暴力的说法。下面怎么做?
这实际上不是树链剖分的事情了,我们只需用这些预先求出的信息用相应的数据结构解决相应的问题就可以了。
来做一道入门题吧(hdu3966):
Aragorn's Story
做法:数剖+线段树
数剖:void init(){
memset(head,-1,sizeof(head));
memset(son,-1, sizeof(son));
tim=edge=0;
}
void addedge(int u,int v){
to[edge]=v;nex[edge]=head[u];head[u]=edge++;
to[edge]=u;nex[edge]=head[v];head[v]=edge++;
}
void dfs1(int u,int father,int d){
dep[u]=d;dep[u]=father;siz[u]=1;
for (int i=head[u]; ~i; i=nex[i]) {
int v=to[i];
if (v==father) {
continue;
}
dfs1(v, u, d+1);
siz[u]+=siz[v];
if (son[u]==-1||siz[v]>siz[son[u]]) {
son[u]=v;
}
}
}
void dfs2(int u,int tp){
tep[u]=tp;tid[u]=++tim;ran[tim]=u;
if (son[u]==-1) {
return;
}
for (int i=head[u]; ~i; i=nex[i]) {
if (to[i]==son[u]||fa[u]==to[i]) {
continue;
}
dfs2(to[i], to[i]);
}
}
线段树:
void tepupdte(int l,int r,int val)
{
while(top[l]!=top[r])
{
if(dep[top[l]]<dep[top[r]]) swap(l,r);
update(tid[top[l]],tid[l],val,1,n,1);
x=fa[top[l]];
}
if(dep[l]>dep[r]) swap(l,r);
update(tid[l],tid[r],val,1,n,1);
}
线段树的其他部分基本类似。。。反正只要知道两件事,一是如何数剖,二是如何用数剖的信息在各种数据结构上做各种操作;
完。。拜拜%%%