我们先引入一些关于“重链剖分”的概念
定义
重儿子:子树节点最多的儿子称之为“重儿子”。若有两个儿子子树大小相同,约定选取编号较小的儿子。
如图,请你写出1、3、4的重儿子
由上面的定义,我们知道1、3、4的重儿子分别为3、2、5
我们用一个数组son[]存u的重儿子编号,按照上图,我们可以列出这张表
重边:每个节点与他的重儿子之间的连边为重边。
那么,我们就把这棵树划为了这样子
重链:将相邻的重边连接起来,就成了一条重链。
就变成了这样子
那么,我们用一个数组top[]来存u所在重链的链头(最高点)。
由上图,可以写出top[]
同样,我们也可以写出下面这棵树的top[]
由前面的定义,我们可以这样划分
所以我们可以写出
算法
我们一共要跑两次dfs,
代码自己写一些
void dfs_son(int u,int fa){
sz[u]=1;
son[u]=0;
for(int i=0;i<=to[u].size();i++){
int v=to[u][i];
if(v==fa) continue;
dfs_son(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]||sz[son[u]]==sz[v]&&v<son[u]){
son[u]=v;
}
}
}
void dfs_top(int u,int fa){
if(son[fa]==u) top[u]==fa;
else top[u]==u;
for(int i=0;i<=to[u].size();i++){
int v=to[u][i];
if(v==fa) continue;
dfs_top(v,u);
}
}
2562
题目描述:
已知一棵二叉树共n个节点,编号1到n,根节点是1号。请对该树进行深度优先搜索(DFS),搜索时优先选重儿子访问,输出DFS序列以及每个节点的DFN。
我们一般称这种dfs叫做重儿子先行dfs,也叫SFS序列化(size first search)。这里我们比上一道题要多令两个数组:dfn[]和id[]
然后多加一个时间戳timer
void dfs_son(int u,int fa){
sz[u]=1;
son[u]=0;
for(int i=0;i<=to[u].size();i++){
int v=to[u][i];
if(v==fa) continue;
dfs_son(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]||sz[son[u]]==sz[v]&&v<son[u]){
son[u]=v;
}
}
}
void dfs_top(int u,int fa){
++timer;
dfn[u]=timer;
id[timer]=u;
if(son[fa]==u) top[u]==fa;
else top[u]==u;
for(int i=0;i<=to[u].size();i++){
int v=to[u][i];
if(v==fa) continue;
dfs_top(v,u);
}
}
2563
题目描述:
已知一棵树共n个节点,编号1到n,根节点是1号。现在共有m个关于“路径剖分”的问询,每个问询形式为:对于节点x和y,从x到y的路径共经过几条不同的重链。
看到这题,首先应该想到的是LCA,交替爬树
首先,若top[u]==top[v],答案为1,那是显然的
若d[top[u]]<d[top[v]],将v跳到p[top[v]]上,直到u、v在同一条链上为止。
那么,我们写一个爬树函数
int query(int u,int v){
int cnt=1;
while(top[u]!=top[v]){
if(d[top[u]]<d[top[v]])
v=p[top[v]];
else u=p[top[u]];
++cnt;
}
return cnt;
}
最后,我们来到我们的性质讨论环节
性质讨论
重链数=叶子数
证明:只需说明重链数与叶子数的双射(一一对应)即可
树的重心一定在根出发的重链上
证明:假设重心G不在链上,则将G改为G的邻居G',使G'更靠近G,取G'为根时,最大子树变小了。
任一点v到根路径上轻边数不超过log(n)
证明:
任一点v到根路径上重边数不超过log(n)
证明:
任一点u到v路径上重边数不超过2log(n)
证明:相当于把LCA(u,v)当作根
彩蛋
最近看到了一道牛掰数学题
是证明,满级证明:
其中用到了不等式,这很好证
(这跟编程无关)