关闭

一只兔子对于树链剖分的理解

标签: 算法
83人阅读 评论(0) 收藏 举报
分类:

树链剖分是做甚的?

某个早上,一只兔子沿一条不规则的曲线冲过来,问了另一只兔子这个问题。
这只兔子当场开始转圈(这是它的一种癖好)。
兔子新的一天开始了…

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);
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    文章分类