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

  • 是什么?
    将树的所有边剖分成重边和轻边从而便于维护边权信息和进行修改,应对一系列大数据树上修改查询问题。
  • 为什么?
    如果不进行剖分代价会很大:对树上边(u,v)中的边权查询修改,必然要将边权建立成搜索树或查询树进行维护,对于所有边建立后对于(u,v)修改时只能将(u,v)中所有边进行遍历修改,最坏代价(退化成链)为n*查询修改代价。而如果把(u,v)中的边变成连续的一段区间,就会大大降低时间代价。
  • 怎么做?
    {正常的题解在这里都会下一堆定义,什么重边重孩子,我觉得不容易理解}
    想把(u,v)这条边变成一段可以查找的连续区间,一定要对边进行一个重新的分类和排序,对于检索也会有要求,如果我不能把(u,v)变成一段全连续的区间,但是如果能变成几部分也是很好的。
    而相对理想的就是变成 log2n 个区间。
    有的人说树链剖分就是把树边hash到 log2n 个区间上。
    下面我只能开始下定义了(由于本人才疏学浅,没法证明根到某一点中轻重边不大于 log2n ,也没法证明这个是怎么想到的)

  • 首先记:
    节点n子树节点数个数size[n];
    节点n深度deep[n];
    节点n父节点fa[n];

  • 重孩子:儿子节点所有孩子中size最大的;
  • 轻孩子:儿子节点中除了重儿子的;
  • 重边:连接重儿子的边;
  • 轻边:连接轻儿子的边;
  • 重链:重边连成的链;
  • 轻链:轻边连成的链;

  • 再定义:
    hson[n]表示n的重儿子标号;
    top[n]表示n在的重链的顶节点,不在重链上为自己标号。
    id[n]表示剖分后的节点标号;

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

  • 实现方法
    其实很简单,第一遍跑一遍dfs求出刚才说的size,deep,hson;
    然后再一遍dfs求出top,id;
    对应id算出val[id]表示id这个点下面连的边的权值。
    对val建检索树,排序树。

  • 重点
    查询的时候这么查:
    对(n,v)这条边:
    如果n,v在一条重边上,就直接查询(id[n],id[v])这一段区间即可。
    如果你,v不在一条重边上,就尽量向一条重边上靠,查询(id[n],id[top[n]]),n再跳到fa[top[n]]上,继续做,知道fa[top[n]]与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]]//找重儿子
    }
}
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;iif(dep[e[i].x]

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

int find(int u,int v){
    int t1=top[u],t2=top[v];
    int ans=0;
    while(t1!=t2){
        if(dep[t1]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就用线段树平衡树维护一下就行了,这里就不附代码。

你可能感兴趣的:(树链剖分)