树链剖分是做甚的?
某个早上,一只兔子沿一条不规则的曲线冲过来,问了另一只兔子这个问题。
这只兔子当场开始转圈(这是它的一种癖好)。
兔子新的一天开始了…
0
树链剖分?什么鬼?
不太懂的小兔子向老兔子(场外观众)求援了。
“咳咳,顾名思义,树链剖分嘛,就是把树剖分成链……”
“这不废话吗?”
“……剖分成若干条链,然后把每条链扔进数据结构里维护……”
“等等树链剖分是干吗的?”
“……以解决如把一棵树上同时增减节点x到节点y的权值之类的问题……”
“……”
(太给力了,你的回答完美的解决了我的问题?)(某熊乱入,惊散兔子)
1
树链剖分有俩个东东
重边 轻边
重边:比较重的那边
轻边:比较轻的那边
嗯,老兔子喝了点伏特加(然而并不是广告)(推)
size(x):以节点x为根的子树的节点个数
重边:节点x与其有最大的size值的那个儿子之间的边
轻边:除了重边外的边全是轻边
“为什么要这样做?”
(老兔子爬起)“Just for fun.啊,不是不是为为为为了使链的数目尽量少,省事。”(又自己摔倒)
“好像有道理…”
于是就有了这两个性质
(1)轻边(u,v)中,size(v)<=size(u)/2
(2)从根到某一点的路径上,轻边、重边条数不超过log n。
2
重边轻边好分,不过怎么连成链呢?
我们把重边连起来:从根节点开始,沿着重边往下找,拉成重链,不在这条重链上?那就从这个节点再往下拉一条。
Talking is easy.Show me the…picture.
3
以下是一些数组
- 之前的size[]数组
- 为了区分重边轻边,我们需要ch[]保存重儿子
- 当然有儿子就有爹——fa[]数组
- 为了区分边所属的链,我们需要top[]数组,保存该节点的所在链的顶端节点
- 为了防止搞混编号,我们需要tid[]数组,保存树中每个节点剖分后的新编号;zrank[]数组,保存当前节点在线段树中的位置
- 嗯我们还需要一个记录深度的dep[]数组,原因…就不用说了…
4
老兔子:”让我翻出70年的雪碧,啊不是15年的代码”
int point[MAX_N],size[MAX_N],top[MAX_N],ch[MAX_N];
int deep[MAX_N],tid[MAX_N],zrank[MAX_N],fa[MAX_N];
int head[MAX_N],to[2*MAX_N],znext[2*MAX_N],edge;//链式前向星
void zinit() {
memset(head,-1,sizeof(head));
memset(ch,-1,sizeof(ch));
num=0;edge=0;
}
void add_edge(int u,int v) {
to[edge]=v,znext[edge]=head[u],head[u]=edge++;
to[edge]=u,znext[edge]=head[v],head[v]=edge++;
}
void dfs1(int u,int pa,int d) {
deep[u]=d;
fa[u]=pa;
size[u]=1;
for(int i=head[u];~i;i=znext[i]) {
int v=to[i];
if(v!=pa) {
dfs1(v,u,d+1);
size[u]+=size[v];
if(ch[u]==-1||size[v]>size[ch[u]])
ch[u]=v;
}
}
}
void dfs2(int u,int tp) {
top[u]=tp;
tid[u]=++num;
zrank[tid[u]]=u;
if(ch[u]==-1) return;
dfs2(ch[u],tp);
for(int i=head[u];~i;i=znext[i]) {
int v=to[i];
if(v!=ch[u]&&v!=fa[u])
dfs2(v,v);
}
}
本文通过轻松幽默的方式介绍了树链剖分的基本概念和技术细节。包括重边轻边的定义、如何构建重链以及所需的数组结构等关键信息。
638

被折叠的 条评论
为什么被折叠?



