首页 > 其他分享 >树链剖分

树链剖分

时间:2024-08-17 13:48:56浏览次数:6  
标签:剖分 fx fy tp 树链 dfn maxn 重链

具体见OI-wiki,下面是一些补充

重链要求是极大的

每个点都在某一个重链中,如果一个点是重子节点,那么其在与其父亲所连的边的重链中,否则在与其重子节点所连的边的重链中

image

这一段的原因:我们走重链是不用关心的,因为同一重链的dfs序是连续的,我们可以用其他数据结构维护,我们只用关心这条路径被划分成了多少条重链以及哪些重链,而重链一旦改变,肯定会经过轻边,也就是说最多划分成\(O(\log n)\)条重链

然后划分链的过程:由于重边属于唯一一条重链,所以我们直接从当前点往上走(沿着重边)走到当前重链的最上面的一个点(这条重链的顶点),然后统计这一段的答案,并循环即可,具体见代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,w[maxn],q;
int cur,Last[maxn],Next[maxn<<1],End[maxn<<1];
int sz[maxn],tp[maxn],son[maxn],dep[maxn],fa[maxn],dfn[maxn],rk[maxn],cnt,maxdfn[maxn];
ll sum[maxn<<2],lazy[maxn<<2];
void add(int x,int y)
{
	End[++cur]=y,Next[cur]=Last[x],Last[x]=cur;
}
void dfs1(int x,int father)
{
	son[x]=-1;
	sz[x]=1;
	for(int i=Last[x];i;i=Next[i])
	{
		int u=End[i];
		if(u==father) continue;
		dep[u]=dep[x]+1;
		fa[u]=x;
		dfs1(u,x);
		sz[x]+=sz[u];
		if(son[x]==-1||sz[son[x]]<sz[u])
		son[x]=u;
	}
}
void dfs2(int x,int t)
{
	tp[x]=t;//tp[x]表示x所在重链的顶点 
	cnt++;
	dfn[x]=cnt;
	rk[cnt]=x;
	if(son[x]==-1) 
	{
		maxdfn[x]=cnt;
		return;
	}
	dfs2(son[x],t);//注意第二个参数是t 
	maxdfn[x]=maxdfn[son[x]];
	for(int i=Last[x];i;i=Next[i])
	{
		int u=End[i];
		if(u!=fa[x]&&u!=son[x]) 
		{
			dfs2(u,u);//注意两个参数都是u 
			maxdfn[x]=max(maxdfn[x],maxdfn[u]);
		}
	}
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		sum[p]=w[rk[l]];
		return;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
void pushdown(int p,int l,int r)
{
	int mid=l+r>>1;
	lazy[p<<1]+=lazy[p],sum[p<<1]+=lazy[p]*(mid-l+1);
	lazy[p<<1|1]+=lazy[p],sum[p<<1|1]+=lazy[p]*(r-mid);
	lazy[p]=0;
}
void modify(int p,int l,int r,int x,int y,ll val)
{
	if(l>y||r<x) return;
	if(l>=x&&r<=y)
	{
		lazy[p]+=val;
		sum[p]+=(r-l+1)*val;
		return;
	}
	pushdown(p,l,r);
	int mid=l+r>>1;
	modify(p<<1,l,mid,x,y,val);
	modify(p<<1|1,mid+1,r,x,y,val);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
void modify1(int x,int y,ll val)
{
	int fx=tp[x],fy=tp[y];
  	while(fx!=fy) 
	{
    	if(dep[fx]>=dep[fy])
      	modify(1,1,n,dfn[fx],dfn[x],val),x=fa[fx];
    	else
      	modify(1,1,n,dfn[fy],dfn[y],val),y=fa[fy];
    	fx=tp[x];
    	fy=tp[y];
  	}
  	if(dfn[x]<dfn[y])
    modify(1,1,n,dfn[x],dfn[y],val);
  	else
    modify(1,1,n,dfn[y],dfn[x],val);
}
ll ask(int p,int l,int r,int x,int y)
{
	if(l>y||r<x) return 0;
	if(l>=x&&r<=y) return sum[p];
	pushdown(p,l,r);
	int mid=l+r>>1;
	return ask(p<<1,l,mid,x,y)+ask(p<<1|1,mid+1,r,x,y);
}
ll query1(int x,int y)
{
	ll res=0;
	int fx=tp[x],fy=tp[y];
  	while(fx!=fy) 
	{
    	if(dep[fx]>=dep[fy])
      	res+=ask(1,1,n,dfn[fx],dfn[x]),x=fa[fx];
    	else
      	res+=ask(1,1,n,dfn[fy],dfn[y]),y=fa[fy];
    	fx=tp[x];
    	fy=tp[y];
  	}
  	if(dfn[x]<dfn[y])
    res+=ask(1,1,n,dfn[x],dfn[y]);
  	else
    res+=ask(1,1,n,dfn[y],dfn[x]);
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
    for(int i=1;i<n;i++)
    {
    	int a,b;
    	scanf("%d%d",&a,&b);
    	add(a,b),add(b,a);
	}
	dfs1(1,0);
	dfs2(1,1);//注意第二个参数是1 
	build(1,1,n);
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int op;
		scanf("%d",&op);
		if(op==1)
		{
			int u,v,k;
			scanf("%d%d%d",&u,&v,&k);
			modify1(u,v,k); 
		 } 
		else if(op==2)
		{
			int u,k;
			scanf("%d%d",&u,&k);
			modify(1,1,n,dfn[u],maxdfn[u],k);
		}
		else if(op==3)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			printf("%lld\n",query1(u,v));
		}
		else
		{
			int u;
			scanf("%d",&u);
			printf("%lld\n",ask(1,1,n,dfn[u],maxdfn[u]));
		}
	}
    return 0;
}

标签:剖分,fx,fy,tp,树链,dfn,maxn,重链
From: https://www.cnblogs.com/dingxingdi/p/18364302

相关文章

  • 树链剖分
    前置知识时间戳在树的\(DFS\)中,以每个节点第一次被访问的顺序,依次给予\(N\)个点\(1-n\)的标记,通常用\(dfn[x]\)表示\(x\)号节点的时间戳。\(DFS\)序进行\(DFS\)时,对每个节点进入递归后以及回溯前各记录一次该点编号,最后产生\(2-n\)的序列即是\(DFS\)序,可设\(L[X]\)和\(R[X]\)......
  • Open3D 三维重建-Delaunay Triangulation (德劳内三角剖分)
    目录一、概述1.1原理1.2实现步骤1.3应用二、代码实现2.1关键函数2.2完整代码三、实现效果3.1原始点云3.2重建后点云Open3D点云算法汇总及实战案例汇总的目录地址:Open3D点云算法与点云深度学习案例汇总(长期更新)-CSDN博客一、概述        德劳内三角剖......
  • 树链剖分
    定义把树剖成一条条不相交的链,对树的操作就转化成了对链的操作概念重儿子:对于每一个非叶子节点,它的儿子中以那个儿子为根的子树节点数最大的儿子为该节点的重儿子轻儿子:对于每一个非叶子节点,它的儿子中非重儿子的剩下所有儿子即为轻儿子重边:连接任意两个重儿子的边叫做重......
  • MATLAB生成各类区域网格剖分
    一、双洞模型代码:hg=[1111111120-20010-10-20210-10020-2012120-2012101111000000001111000000000000111122221111];ug=[1111010-1......
  • 二叉树链式结构代码讲解与实现
    本片将续之前的文章二叉树的概念进行二叉树基本操作的实现,二叉树oj题将在下篇文章讲解。目录a.创建二叉树代码:一、二叉树的遍历1.1前序、中序以及后序遍历代码:如图:(前序遍历递归图解)测试代码:二、节点个数以及高度2.1 二叉树节点个数思想:要求二叉树的总节点个数,左......
  • MATLAB: 使用Delaunay三角剖分构建点云网格
    在计算机图形学和计算几何学中,Delaunay三角剖分a是一种常用的方法,用于将点云数据转换为三角形网格,MATLAB提供了内置函数来执行Delaunay三角剖分,并生成适用于点云可视化和分析的三角网格,本文将介绍如何使用MATLAB进行点云的Delaunay三角剖分,并提供相应的源代码。步骤一:导入点云......
  • 树链剖分
    P3379【模板】最近公共祖先(LCA)dfs1:处理一个点的深度、父结点、子树大小,重儿子。dfs2:记录每个点的最顶部。query:哪个top的深度小跳哪个。#include<bits/stdc++.h>usingnamespacestd;constintN=500010,M=1000010;structedge{intto,next;}e[......
  • 长链剖分笔记
    与轻重链剖分相似.dfs1:高度\(h\)、\(son\);dfs2:\(top\).性质1:到根最多\(O(\sqrtn)\)条轻边.(证明:长链长度最坏情况:1,2,3...)性质2:\(x\)的\(k\)级祖先\(y\)所在的长链长度\(\gek\).(证明:若非,则\(y-x\)是一条更长的链,矛盾.)树上\(k\)级祖先\(O(n\logn)-O(1)\):......
  • 树链剖分
    引言第一次接触树链/重链剖分的时候还是学习\(Lca\),没系统性的看过剖分,今天刚重新学习了一下,还是比较神奇的,没想到一个树形结构能有这么多种神奇的操作,总的来说,树链剖分还是比较重要的一个策略正文定义先给出图示首先我们给出以下几个定义:重儿子,对于一个......
  • [学习笔记] 长链剖分 - 图论
    长链剖分字面意思,不同于重链剖分,每次选取最长的树链进行剖分,直到剖完为止。其原理和重链剖分相似。建议学习长链剖分前,先学习重链剖分。重链剖分能做的,长链剖分都能做(当然不包括找重儿子),长链剖分还能以\(O(nlogn)-O(1)\)的优秀复杂度找到\(k\)级祖先(当前节点的第\(k\)个......