树链剖分简介及补题1

来源:互联网 发布:java常见注解 编辑:程序博客网 时间:2024/06/04 18:58

   先简单介绍一下树链剖分;

   树链剖分可以看成对树的预处理,对不同结构的树的所有节点用标号tid【i】表示,求解一个特殊标号tid【i】使得用这一组特殊标号对应到的相应的数据结构后对查询和修改的复杂度都优化为logn^2的级别的过程即可叫做对树的剖分。。一般的是用启发式数剖的方法求解tid【i】,即定义重儿子,轻儿子,重链,轻链相应概念,按照一遍dfs求出每个点的重儿子(没有儿子的默认为0),和每个点的深度dep,在求解这些信息后,再dfs一遍,并且先dfs重儿子,用每个重链的深度最小的点的tid【i】表示这个重链,同时因为每次都是按照重儿子的顺序dfs,所以重链上的编号都是连续的,即将重链为一个用两个信息维护的区间,对于轻儿子直接把它映射为单点,即重链分治,轻链暴力的说法。下面怎么做?

这实际上不是树链剖分的事情了,我们只需用这些预先求出的信息用相应的数据结构解决相应的问题就可以了。

  来做一道入门题吧(hdu3966):

                                                                                                              Aragorn's Story

Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of enemies who want to invade his kingdom. As Aragorn knows, the enemy has N camps out of his kingdom and M edges connect them. It is guaranteed that for any two camps, there is one and only one path connect them. At first Aragorn know the number of enemies in every camp. But the enemy is cunning , they will increase or decrease the number of soldiers in camps. Every time the enemy change the number of soldiers, they will set two camps C1 and C2. Then, for C1, C2 and all camps on the path from C1 to C2, they will increase or decrease K soldiers to these camps. Now Aragorn wants to know the number of soldiers in some particular camps real-time.

做法:数剖+线段树

数剖:void init(){
    memset(head,-1,sizeof(head));
    memset(son,-1, sizeof(son));
    tim=edge=0;
}
void addedge(int u,int v){
    to[edge]=v;nex[edge]=head[u];head[u]=edge++;
    to[edge]=u;nex[edge]=head[v];head[v]=edge++;
}
void dfs1(int u,int father,int d){
    dep[u]=d;dep[u]=father;siz[u]=1;
    for (int i=head[u]; ~i; i=nex[i]) {
        int v=to[i];
        if (v==father) {
            continue;
        }
        dfs1(v, u, d+1);
        siz[u]+=siz[v];
        if (son[u]==-1||siz[v]>siz[son[u]]) {
            son[u]=v;
        }
    }
}
void dfs2(int u,int tp){
    tep[u]=tp;tid[u]=++tim;ran[tim]=u;
    if (son[u]==-1) {
        return;
    }
    for (int i=head[u]; ~i; i=nex[i]) {
        if (to[i]==son[u]||fa[u]==to[i]) {
            continue;
        }
        dfs2(to[i], to[i]);
    }
}

线段树:

void tepupdte(int l,int r,int val)
{
    while(top[l]!=top[r])
    {
        if(dep[top[l]]<dep[top[r]]) swap(l,r);
        update(tid[top[l]],tid[l],val,1,n,1);
        x=fa[top[l]];
    }
    if(dep[l]>dep[r]) swap(l,r);
    update(tid[l],tid[r],val,1,n,1);
}

线段树的其他部分基本类似。。。反正只要知道两件事,一是如何数剖,二是如何用数剖的信息在各种数据结构上做各种操作;


完。。拜拜%%%