• 树链剖分


    前言

    树链剖分:在一颗树上两点之间的路径的修改、求值。

    原理

    将一课树分成若干条链,将它们连起来,形成一条链,再用线段树等方法来维护、求值。


    定义

    在熟练剖分中,会使用到很多数组,这是它们的作用:

    size[x]:在以x为根的子树中的节点的个数
    son[x]:x的重儿子
    deep[x]:x的深度
    fa[x]:x的父亲
    top[x]:x所在的链的顶部节点
    aft[x]:x在合成的新链中的编号
    bef[x]:x在树上的编号
    

    那么重儿子就是某个节点的儿子中size[]值最大的节点,重边就是它们的之间的边,由重边连起来的链就是重链。那么其余的节点就是轻儿子轻边就是它们的之间的边。

    性质

    一、轻边(u,v),size(v)<=size(u)/2。
    二、从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。

    实现

    预处理

    首先用一个dfs把size[]、son[]、deep[]、fa[]求出来。

    int dg(int x)
    {
    	size[x]=1;
    	int mx=0;
    	for(int i=last[x];i;i=next[i])
    	{
    		if(to[i]!=fa[x])
    		{
    			fa[to[i]]=x;
    			deep[to[i]]=deep[x]+1;
    			dg(to[i]);
    			size[x]+=size[to[i]];
    			if(size[to[i]]>mx)
    			{
    				mx=size[to[i]];
    				son[x]=to[i];
    			}
    		}
    	}
    }
    

    再用一个dfs求top[]、aft[]、bef[]以及重链
    从每一个轻儿子开始,沿着重边往下拉,直到拉到叶子节点为止。
    最后,将所有的重链首尾相接,形成一条新的链,按照顺序,给每一个点一个新的编号。

    int dg1(int x)
    {
    	aft[x]=++tot;
    	d[tot]=x;
    	if(top[x]==0)
    	{
    		top[x]=x;
    	}
    	if(son[x])
    	{
    		top[son[x]]=top[x];
    		dg1(son[x]);
    	}
    	for(int i=last[x];i;i=next[i])
    	{
    		if(to[i]!=fa[x] && to[i]!=son[x])
    		{
    			dg1(to[i]);
    		}
    	}
    }
    

    修改、查询操作

    当修改或查询u、v两个点的路径时
    (1)当u、v在同一条重链上,即top[u]=top[v],显然直接修改aft[u]和aft[v]之间的值。
    (2)当u、v不在同一条重链上,即top[u]<>top[v],如果deep[top[u]]>deep[top[v]],就修改aft[top[u]]和aft[u]之间的值,并将u赋值为fa[top[u]]。反之。
    反复做(2)操作,直到变成(1)的情况。
    时间复杂度O(NlogN)。


    【ZJOI2008】树的统计
    这道题可以用来练手,挺好滴。

  • 相关阅读:
    Docker 部署项目
    Python+Pywinauto+Lackey 实现PC端.exe 自动化测试
    03_Fiddler 导出jmx文件
    02_Postman 中文汉化版
    07_Linux系统(Centos)安装tomcat和部署Web项目
    05_oracel题集
    02_appium基本使用
    01_appium的安装
    02_Monkey使用
    01_Monkey安装
  • 原文地址:https://www.cnblogs.com/chen1352/p/9026692.html
Copyright © 2020-2023  润新知