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],然后直接树链剖分做就可以了。

  • 相关阅读:
    Python使用 odbc、jdbc与 Object Relational Mapping (ORM)进行数据库开发
    Tensorflow安装
    学生作业
    大学课程推荐
    人工智能的开发工具
    android开发
    jdbc-odbc桥
    开博随记
    利用jq实现自适应边缘情况的气泡Tip
    一篇完整的FlexBox布局指南
  • 原文地址:https://www.cnblogs.com/Akaina/p/11220780.html
Copyright © 2011-2022 走看看