zoukankan      html  css  js  c++  java
  • 树链剖分

    树链剖分

    前言:虽然noip基本不考,但我觉得还是多少学一点吧


    问题模型

    对于树上的某条路径,进行一系列操作(类似线段树上的操作)


    实现原理

    恰如其名,将树剖分成一段又一段的区间(树链),便于我们进行线段树的操作(树上的线段树操作)。将树分为重链和轻链,重链的dfs序(与其他dfs序不同,树剖的dfs序会优先落在重链上)多为一段连续的区间。因此当解决两点间路径上的查询和修改问题时,可以用类似于LCA(其实区别较大,LCA是通过倍增来跳,而树剖是通过重链来跳)的方法,将两点之间的路径剖分成多条首尾相连的树链,每条树链上的点所在区间是连续的,进而进行线段树的操作。(关于重链,轻链的定义请自行查询)


    代码实现

    进行两遍dfs,第一遍储存size[],father[],son[](重儿子),dep[]

    第二遍储存top[](所在重路径的顶部节点),seg[](在线段树中的位置),rev[](rev[seg[x]]=x)

    void dfs1(int u,int father)
    {
        ssize[u]=1;
        dep[u]=dep[father]+1;
        fa[u]=father;
        for(int i=fir[u];i;i=nxt[i])
        {
            int v=vv[i];
            if(v==father)continue;
            dfs1(v,u);
            ssize[u]+=ssize[v];
            if(ssize[v]>ssize[son[u]])son[u]=v;
        }
    }
    /*
    void dfs2(int u,int father)
    {
        if(son[u])
        {
            seg[son[u]]=++seg[0];
            rev[seg[son[u]]]=son[u];
            top[son[u]]=top[u];
            dfs2(son[u],u);
        }
        for(int i=fir[u];i;i=nxt[i])
        {
            int v=vv[i];
            if(!top[v])
            {
                seg[v]=++seg[0];
                rev[seg[v]]=v;
                top[v]=v;
                dfs2(v,u);
            }
        }
    }*/
    

    **(update)(11)(15)

    最近了解到(dfs2)的第二种写法,不但码量少,而且可以不用在(main)函数里初始化(root)(dfs)

    void dfs2(int u, int tp)
    {
        seg[u] = ++seg[0]; rev[seg[0]] = u; top[u] = tp;
        if(son[u] != 0) dfs(son[u], tp);
        for(int i = fir[u]; i; i = nxt[i])
        {
            int v = vv[i];
            if(v == fa[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }
    

    **

    当查询两点之间的路径时,通过每次top[]较深的点往上跳,当跳到同一条重链上时,深度较浅的一点即为两点的LCA,再将两点的路径转化为多条重链(跳跃的过程),分别进行区间查询。

    void ask(int x,int y)
    {
        int fx=top[x],fy=top[y];
        while(fx!=fy)
        {
            if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
            query(1,1,seg[0],seg[fx],seg[x]);
            x=father[fx];fx=top[x];
        }
        if(dep[x]>dep[y])swap(x,y);
        query(1,1,seg[0],seg[x],seg[y]);
    }
    

    例题

    [Noi2015]软件包管理器

    解析:虽然是NOI的题,但却是一道Day1 T2的除了题意比较冗长外的极水的树链剖分裸题。安装软件包就是将根节点到x节点的路径上所有值变为1,卸载软件包就是将x节点和它所有子树节点的值变为0,而x和它的子树节点在树链剖分中恰好是一段连续的区间[seg[x],seg[x]+size[x]-1],然后直接树链剖分做就可以了。

  • 相关阅读:
    vmware ubuntu 异常关机无法连接到网络
    Speed up GCC link
    常用的一些解压命令
    Log4j 漏洞复现
    Test Case Design method Boundary value analysis and Equivalence partitioning
    CCA (Citrix Certified Administrator) exam of “Implementing Citrix XenDesktop 4”
    What is Key Word driven Testing?
    SAP AGS面试小结
    腾讯2013终端实习生一面
    指针的引用
  • 原文地址:https://www.cnblogs.com/Akaina/p/11220780.html
Copyright © 2011-2022 走看看