首页 > 编程语言 >算法:树链剖分

算法:树链剖分

时间:2023-10-12 09:44:21浏览次数:48  
标签:rt sz 剖分 int top 树链 dep 算法 id

去年就看过树链剖分的视频了,当时连树状数组,线段树都没学,对树的 dfs 也一知半解,所以基本完全听不懂。昨天又重新看了一般,感觉思路挺简单,应该比线段树简单吧,从用树链剖分求 LCA 来看确实是这样的,但是没有想到的是用线段树维护树链剖分。QAQ 这应该是我打过最长的代码吧!(3K)

树链剖分

只讲一下基本思路,重在感性理解。

image

这应该是最典型的图了吧。

然后按遍历顺序把它们变成一个线性的数组

1 2 3 4 5 6 7 8
1 3 6 8 7 2 5 4

然后就变成这样一个数组了,感性理解,把剖开的链按遍历顺序拼在一起

为什么要这么做,应为我们要用高级数据结构来维护,比如线段树

可以实现查询并更改在在树上的任意两点路径的权值和任意子树的权值。

例题:树链剖分 - 重链剖分

image

AC 代码:

#include<bits/stdc++.h>
using namespace std;
#define lc u<<1
#define rc u<<1|1
typedef long long LL;
const int N = 100010;
int n,m,root,p;
int w[N];
vector<int >e[N];
int fa[N],dep[N],sz[N],son[N];
int top[N],id[N],nw[N],cnt;

void dfs1(int u){//处理fa,deo,sz,son 
	dep[u]=dep[fa[u]]+1;
	sz[u]=1;
	for(auto v:e[u]){
		if(v==fa[u]) continue;
		fa[v]=u;
		dfs1(v);
		sz[u]+=sz[v];
		if(sz[son[u]<sz[v]]) son[u]=v;
	}
}

void dfs2(int u,int t){//处理top,id,nw
	top[u]=t;id[u]=++cnt;
	nw[cnt]=w[u];
	if(!son[u]) return;
	dfs2(son[u],t);
	for(auto v:e[u]){
		if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
//-----------------------------------------------
struct tree{
	int l,r;
	LL add,sum;
}tr[N*4];

void pushup(int rt){
	tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
}

void build(int rt,int l,int r){
	tr[rt]={l,r,0,nw[r]};
	if(l==r) return;
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	pushup(rt);
}

void pushdown(int u){
	if(tr[u].add){
		tr[lc].sum+=tr[u].add*(tr[lc].r-tr[lc].l+1);
		tr[rc].sum+=tr[u].add*(tr[rc].r-tr[rc].l+1);
		tr[lc].add+=tr[u].add;
		tr[rc].add+=tr[u].add;
		tr[u].add=0;
	} 
}

void update(int rt,int l,int r,int k){//线段树上更新 
	if(l<=tr[rt].l&&r>=tr[rt].r){
		tr[rt].add+=k;
		tr[rt].sum+=k*(tr[rt].r-tr[rt].l+1);
		return ;
	}
	pushdown(rt);//不能完全包含更改区间,先把懒标记下传 
	int mid=tr[rt].l+tr[rt].r>>1;
	if(l<=mid) update(rt<<1,l,r,k);
	if(r>mid) update(rt<<1|1,l,r,k);
	pushup(rt);
}

void update_path(int x,int y,int z){
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		update(1,id[top[y]],id[y],z);
		y=fa[top[y]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	update(1,id[x],id[y],z);
}

void update_tree(int u,int k){
	update(1,id[u],id[u]+sz[u]-1,k);//注意减一 
}

LL query(int u,int l,int r){//线段树上查询 
	if(l<=tr[u].l&&tr[u].r<=r){
		return tr[u].sum;
	}
	pushdown(u);
	int mid=tr[u].l+tr[u].r>>1;
	LL res=0;
	if(l<=mid) res+=query(lc,l,r);
	if(r>mid) res+=query(rc,l,r);
	return res;
}

LL query_path(int x,int y){
	LL res=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		res+=query(1,id[top[y]],id[y]);
		y=fa[top[y]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	res+=query(1,id[x],id[y]);
	return res;
}

LL query_tree(int u){
	return query(1,id[u],id[u]+sz[u]-1);
}

int main(){
	scanf("%d%d%d%d",&n,&m,&root,&p);
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
	}
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		e[x].emplace_back(y);
		e[y].emplace_back(x);
	}
//	cout<<"finish cin"<<"\n";
	fa[root]=0;
	dfs1(root);
	dfs2(root,root);
//	cout<<"finish dfs"<<"\n";
	build(1,1,n);
//	cout<<"finish build"<<"\n";
	while(m--){
		int op,x,y,z;
		scanf("%d%d",&op,&x);
		if(op==1){
			scanf("%d%d",&y,&z);
			update_path(x,y,z);
		}else if(op==2){
			scanf("%d",&y);
			printf("%d\n",query_path(x,y)%p);
		}else if(op==3){
			scanf("%d",&z);
			update_tree(x,z);
		}else if(op==4){
			printf("%d\n",query_tree(x)%p);
		}
	}
	return 0;
}

先这样,代码越长越容易寄!

标签:rt,sz,剖分,int,top,树链,dep,算法,id
From: https://www.cnblogs.com/alloverzyt/p/17758758.html

相关文章

  • 【算法】游戏中的学习,使用c#面向对象特性控制游戏角色移动
    最近,小悦的生活像是一首繁忙的交响曲,每天忙得团团转,虽然她的日程安排得满满当当,但她并未感到充实。相反,她很少有时间陪伴家人,这让她感到有些遗憾。在周五的午后,小悦的哥哥突然打来电话,他的声音里充满了焦虑。“小悦,我有个事情想拜托你。”哥哥的声音传来。小悦不禁有些疑惑,哥哥......
  • WPF 笔迹算法 从点集转笔迹轮廓
    本文将告诉大家一些笔迹算法,从用户输入的点集,即鼠标轨迹点或触摸轨迹点等,转换为一个可在界面绘制显示笔迹画面的基础数学算法。尽管本文标记的是WPF的笔迹算法,然而实际上本文更侧重基础数学计算,理论上可以适用于任何能够支持几何绘制的UI框架上,包括UWP或WinUI或UNO或MA......
  • 基于亚奈奎斯特采样和SOMP算法的平板脉冲响应空间插值matlab仿真
    1.算法运行效果图预览 2.算法运行软件版本matlab2022a 3.算法理论概述     平板脉冲响应(PulseResponse)是通信和雷达等领域中的重要参数,它描述了信号在空间中传播的特性。在现实应用中,获取完整的脉冲响应通常是耗时且昂贵的。基于亚奈奎斯特采样和SOMP算法的平板脉......
  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
    704.二分查找链接:https://leetcode.cn/problems/binary-search/description/思路:关键是定义清楚区间边界,想清楚middle在计算中是否可能取到左边界or右边界。若采用闭区间,则middle可能等于左/右边界值。27.移除元素链接:https://leetcode.cn/problems/remove-element/思路:暴......
  • 弗洛伊德(Floyd's)算法—解决最短路径经典算法
    弗洛伊德算法(Floyd'salgorithm)是一种用于解决图中最短路径问题的经典算法。由美国计算机科学家罗伯特·弗洛伊德于1962年提出,该算法通过动态规划的思想,在图中寻找任意两个节点之间的最短路径,具有广泛的应用。本文将详细介绍弗洛伊德算法的原理、实现细节以及应用案例。四、复杂度......
  • 代码随想录算法训练营第一天(python) | 704. 二分查找、27. 移除元素。
    Leetcode704二分查找题目链接:704二分查找关键点思路:1、是否要进入到while部分的代码是left<=right还是left<right,看[left,right]是否是合法区间.例如[1,1]是合法区间,取<=;[1,1)非合法区间,取<。2、缩小区间时,考虑边界是否已经比较过。左闭右闭区......
  • 【RF分类】基于粒子群优化随机森林PSO-RF实现数据分类算法研究附matlab代码可直接运行
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 【leach协议】基于粒子群算法改进能量均衡高效WSN的LEACH协议附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 《算法学习专栏》—— DP问题之背包模型
    2023年10月11日更新于2023年10月11日一、前言本栏,为背包模型,题目主要来源日常,目前主要来源于Acwing的提高课。希望以后做到背包的题目,也能加进来,不断完善。使用的分析方法均为闫式DP分析法。字臭。。。希望能用手写板慢慢写的好看。二、背包模型2.1目前的模型01背包模型......
  • 花朵识别系统Python+TensorFlow+Django+网页界面+算法模型
    一、介绍花朵识别系统,使用Python作为主要编程语言进行开发,使用TensorFlow搭建卷积神经网络算法模型,并基于多种花朵数据集进行模型训练,最后得到一个精度较高的h5模型文件。并基于Django框架搭建网页端可视化操作界面。实现用户上传一张花朵图片,识别其名称。二、效果图片展示......