树链剖分

思想:把树拆分成 一条一条的线段(利用重链) 在变成1条线(从而利用线段树处理)

应用: 主要是 对数的lca求解,并且对lca的路径上进行查询和修改。

 

两次dfs 进行构建

第一次 为拆分树做准备: 重儿子 son,每一个点的深度,父亲,尺寸;

 

int dep[M],vis[M],fa[M],son[M],sz[M];
void dfs1(int a,int f)
{
    vis[a]=1;
    dep[a]=dep[f]+1;
    fa[a]=f;
    sz[a]++;
    int mx=0;
    for(ri i=0;i<p[a].size();i++)
    {
        int b=p[a][i];
        if(vis[b]) continue;
        dfs1(b,a);
        sz[a]+=sz[b];
        if(sz[b]>mx)
        {
            mx=sz[b];
            son[a]=b;
        }
    }
}
View Code

 

第二次  进行拆树 给每一个节点建立一个新id (用于线段树)

 

void dfs2(int a,int f)
{
    vis[a]=1;
    top[a]=f;
    id[a]=++cur;
    addr[cur]=a;
    if(son[a]) dfs2(son[a],f);
    for(ri i=0;i<p[a].size();i++)
    {
        int b=p[a][i];
        if(vis[b]) continue;
        dfs2(b,b);
    }
    hao[a]=cur;
}
View Code

LCA:

 

void x_lca(int a,int b)
{
    while(top[a]!=top[b])
    {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        xiu(id[top[a]],id[a],1,x);
        a=fa[top[a]];
    }
    if(dep[a]<dep[b]) swap(a,b);
    xiu(id[b],id[a],1,x);
}
long long q_lca(int a,int b)
{
    long long ans=0;
    while(top[a]!=top[b])
    {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        ans+=qu(id[top[a]],id[a],1);
        a=fa[top[a]];
    }
    if(dep[a]<dep[b]) swap(a,b);
    ans+=qu(id[b],id[a],1);
    return ans;
}
View Code

 

线段树:

struct dian{
    int l,r,val;
}p[M*4];
int lz[M*4];
int val[M];
void up(int i)
{
    p[i].val=p[i<<1].val+p[i<<1|1].val;
}
void build(int l,int r,int i)
{
    p[i].l=l,p[i].r=r;
    if(l==r)
    {
        p[i].val=val[addr[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,i<<1);
    build(mid+1,r,i<<1|1);
    up(i);
}
void down(int i)
{
    lz[i<<1]+=lz[i];
    lz[i<<1|1]+=lz[i];
    p[i<<1].val+=(p[i<<1].r-p[i<<1].l+1)*lz[i];
    p[i<<1|1].val+=(p[i<<1|1].r-p[i<<1|1].l+1)*lz[i];
    lz[i]=0;
}
void xiu(int l,int r,int i,int x)
{
    if(p[i].l>r||p[i].r<l) return ;
    if(l<=p[i].l&&p[i].r<=r)
    {
        lz[i]+=x;
        p[i].val+=(p[i].r-p[i].l+1)*x;
        return ;
    }
    if(lz[i]) down(i);
    xiu(l,r,i<<1,x);
    xiu(l,r,i<<1|1,x);
    up(i);
}
long long qu(int l,int r,int i)
{
    if(p[i].l>r||p[i].r<l) return 0;
    if(l<=p[i].l&&p[i].r<=r)
    {
        return p[i].val;
    }
    if(lz[i]) down(i);
    return qu(l,r,i<<1)+qu(l,r,i<<1|1);
}
View Code

 

 后记:

  • dfs时 vis记得写,并且在2次bfs中间 记得对vis清零
  • sz本身节点要+1,儿子的尺寸也要加上去
  • 重链的更新 (b,f) (b,b);
  • 先定义变量,在写dfs,写dfs 反过去看看是否变量都处理了没有
  • LCA,用线段树的时候 都是写 ID,不是本身的!!!
  • 是dep[top[a]]的深度,不是dep【a】;当然后面就是那个
  • 深度小在前面
  • build时,是 val【addr【l】】,不是直接L,这个l,r是新的id
  • 其他的注意都是线段树的了。 上下+return

 

  

posted @ 2019-10-16 22:10  VxiaohuanV  阅读(149)  评论(0编辑  收藏  举报