您的位置:首页 > 其它

【树链剖分】树链剖分讲解

2016-05-16 22:47 218 查看
是什么?

将树的所有边剖分成重边和轻边从而便于维护边权信息和进行修改,应对一系列大数据树上修改查询问题。

为什么?

如果不进行剖分代价会很大:对树上边(u,v)中的边权查询修改,必然要将边权建立成搜索树或查询树进行维护,对于所有边建立后对于(u,v)修改时只能将(u,v)中所有边进行遍历修改,最坏代价(退化成链)为n*查询修改代价。而如果把(u,v)中的边变成连续的一段区间,就会大大降低时间代价。

怎么做?

{正常的题解在这里都会下一堆定义,什么重边重孩子,我觉得不容易理解}

想把(u,v)这条边变成一段可以查找的连续区间,一定要对边进行一个重新的分类和排序,对于检索也会有要求,如果我不能把(u,v)变成一段全连续的区间,但是如果能变成几部分也是很好的。

而相对理想的就是变成log2n个区间。

有的人说树链剖分就是把树边hash到log2n个区间上。

下面我只能开始下定义了(由于本人才疏学浅,没法证明根到某一点中轻重边不大于log2n,也没法证明这个是怎么想到的)

首先记:

节点n子树节点数个数size


节点n深度deep
;

节点n父节点fa
;

重孩子:儿子节点所有孩子中size最大的;

轻孩子:儿子节点中除了重儿子的;

重边:连接重儿子的边;

轻边:连接轻儿子的边;

重链:重边连成的链;

轻链:轻边连成的链;

再定义:

hson
表示n的重儿子标号;

top
表示n在的重链的顶节点,不在重链上为自己标号。

id
表示剖分后的节点标号;

性质

从根节点到任意节点路径上轻重边数量不大于log2n(别问我为什么);

实现方法

其实很简单,第一遍跑一遍dfs求出刚才说的size,deep,hson;

然后再一遍dfs求出top,id;

对应id算出val[id]表示id这个点下面连的边的权值。

对val建检索树,排序树。

重点

查询的时候这么查:

对(n,v)这条边:

如果n,v在一条重边上,就直接查询(id
,id[v])这一段区间即可。

如果你,v不在一条重边上,就尽量向一条重边上靠,查询(id
,id[top
]),n再跳到fa[top
]上,继续做,知道fa[top
]与v在一条重链上,就回到了上一种情况。

这里也有一些细节,因为题中往往给的边都不是按深度给的,所以对于u,v,要注意深度关系,进行交换

好了,这样我们就成功解决了对树上修改查询边权或点的问题。

下面放上代码:

vector<int> v[MAXN];
int size[MAXN],dep[MAXN],val[MAXN],id[MAXN],hson[MAXN],top[MAXN],fa[MAXN];//定义
int edge=1,num=1;
struct tree{
int x,y,z;
void init(){scanf("%d%d%d",&x,&y,&z);}
}e[MAXN];//起点重点权值
void dfs1(int d,int f,int u){//d:深度 f:父节点 u:当前节点
dep[u]=d;
size[u]=1;
hson[u]=0;
fa[u]=f;
for(vector<int>::iterator it=v[u].begin();it!=v[u].end();it++){
if(*it==f) continue;
dfs1(d+1,u,*it);
size[u]+=size[*it];
if(size[hson[u]]<size[*it])
hson[u]=*it;//找重儿子
}
}
void dfs2(int u,int tp){
top[u]=tp;//赋值top
id[u]=num++;//赋值id
if(hson[u]) dfs2(hson[u],tp);//优先重儿子
for(vector<int>::iterator it=v[u].begin();it!=v[u].end();it++){
if(*it==fa[u]||*it==hson[u]) continue;
dfs2(*it,*it);
}
}


赋值val:节点与父亲的边

for(int i=1;i<n;i++){
if(dep[e[i].x]<dep[e[i].y])
swap(e[i].x, e[i].y);
val[id[e[i].x]] = e[i].z;
}


查询:只要不在一条重链上,(深度大的)就向父节点靠

int find(int u,int v){
int t1=top[u],t2=top[v];
int ans=0;
while(t1!=t2){
if(dep[t1]<dep[t2]){
swap(t1,t2);
swap(u,v);
}
ans=max(query(1,id[t1],id[u]),ans);
u=fa[t1];
t1=top[u];
}
if(u==v) return ans;
if (dep[u] > dep[v]) swap(u, v);
ans = max(query(1,id[hson[u]], id[v]), ans);
return ans;
}


val就用线段树平衡树维护一下就行了,这里就不附代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树链剖分