【学习笔记】线段树合并

news2024/9/22 19:27:52

前言

一般来说,线段树会有 O ( n ) O(n) O(n) 个节点。但是有的时候,整棵线段树就只进行了一次插入操作,这样只会有 O ( l o g n ) O(logn) O(logn) 个节点。
处理树上问题时,我们有时需要把儿子的信息合并到父亲节点。这个时候可以使用 dsu on tree,常数小,但复杂度多一个 l o g log log。或者,我们也可以使用线段树合并。

算法过程

必须使用动态开点线段树。
我们需要把线段树 v v v 合并到线段树 u u u

  1. 传入两棵线段树的根,以及它们代表的区间范围。
  2. 如果此时区间长度为1,那么就直接合并两个点。
  3. 如果两棵线段树都有左儿子,那么就尝试合并左儿子
  4. 如果只有 v v v 有左儿子,那么令 u u u 的左儿子等于 v v v 的左儿子
  5. 右儿子类似

核心代码

void merge(int u,int v,int st,int ed)
{
	if(st==ed)
	{
		tr[u].num+=tr[v].num;
		tr[u].sum+=tr[v].sum;
		tr[u].val+=tr[v].val;
		return;
	}
	int mid=st+ed>>1;
	if(tr[u].ls&&tr[v].ls)
	{
		merge(tr[u].ls,tr[v].ls,st,mid);
	}
	else if(tr[v].ls)
	{
		tr[u].ls=tr[v].ls;
	}
	if(tr[u].rs&&tr[u].rs)
	{
		merge(tr[u].rs,tr[v].rs,mid+1,ed);
	}
	else
	{
		tr[u].rs=tr[v].rs;
	}
	update(u);
}

洛谷P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并

在这里插入图片描述
在这里插入图片描述

思路

树上差分+线段树合并
最朴素的想法是每个点开一个vector,对于每一次发放救济粮就对那一种救济粮进行树上差分。然后最后dfs一遍,每次暴力合并儿子的vector。
但显然这样就会TLE,时间复杂度 O ( n 2 ) O(n^2) O(n2)
我们不妨用权值线段树代替vector,合并儿子信息时使用线段树合并,这样就可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7,inf=1e9,S=20;
vector<vector<int>> e,fa,a;
vector<int> dep;
int n,m;
struct seg
{
	int ls,rs,mx,id;
	seg()
	{
		ls=rs=id=0;
		mx=0;
	}
	seg(int a,int b,int c,int d):ls(a),rs(b),mx(c),id(d)
	{
	}
	friend bool operator < (const seg &a,const seg &b)
	{
		return a.mx==b.mx?a.id>b.id:a.mx<b.mx;
	}
	friend seg operator + (const seg &a,const seg &b)
	{
		return seg(a.ls,a.rs,a.mx+b.mx,a.id);
	}
};
vector<seg> tr;
void update(int u)
{
	int ls=tr[u].ls,rs=tr[u].rs;
	if(tr[ls]<tr[rs])
	{
		tr[u].mx=tr[rs].mx;
		tr[u].id=tr[rs].id;
	}
	else
	{
		tr[u].mx=tr[ls].mx;
		tr[u].id=tr[ls].id;
	}
}
void merge(int u,int v,int st,int ed)
{
	if(st==ed)
	{
		tr[u]=tr[u]+tr[v];
		return;
	}
	int mid=st+ed>>1;
	if(tr[u].ls&&tr[v].ls)
		merge(tr[u].ls,tr[v].ls,st,mid);
	else if(tr[v].ls)
		tr[u].ls=tr[v].ls;
	if(tr[u].rs&&tr[v].rs)
		merge(tr[u].rs,tr[v].rs,mid+1,ed);
	else if(tr[v].rs)
		tr[u].rs=tr[v].rs;
	update(u);
}
void insert(int u,int st,int ed,int x,int t)
{
	if(st==ed&&ed==x)
	{
		tr[u].mx+=t;
		tr[u].id=x;
		return;
	}
	int mid=st+ed>>1;
	if(x<=mid)
	{
		if(!tr[u].ls)
		{
			tr.push_back(seg());
			tr[u].ls=tr.size()-1;
//			cerr<<tr.size()-1<<"\n";
		}
		insert(tr[u].ls,st,mid,x,t);
	}
	else
	{
		if(!tr[u].rs)
		{
			tr.push_back(seg());
			tr[u].rs=tr.size()-1;
//			cerr<<tr.size()-1<<"\n";
		}
		insert(tr[u].rs,mid+1,ed,x,t);
	}
	update(u);
}
void dfs1(int u)
{
	dep[u]=dep[fa[u][0]]+1;
	for(auto v:e[u])
	{
		if(v==fa[u][0]) continue;
		fa[v][0]=u;
		dfs1(v);
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])
		swap(x,y);
	for(int i=S-1; i>=0; i--)
	{
		if(dep[fa[x][i]]>=dep[y])
			x=fa[x][i];
	}
	if(x==y) return x;
	for(int i=S-1; i>=0; i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
vector<int> ans;
void dfs2(int u)
{
	for(auto v:e[u])
	{
		if(v==fa[u][0]) continue;
		dfs2(v);
		merge(u,v,1,N);
	}
	for(auto x:a[u])
	{
		if(x>0)
			insert(u,1,N,x,1);
		else
			insert(u,1,N,-x,-1);
	}
	ans[u]=tr[u].mx>0?tr[u].id:0;
}
void O_o()
{
	cin>>n>>m;
	e.assign(n+1,vector<int>());
	for(int i=1; i<n; i++)
	{
		int x,y;
		cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	fa.assign(n+1,vector<int>(S));
	dep.assign(n+1,0);
	dfs1(1);
	for(int j=1; j<S; j++)
	{
		for(int i=1; i<=n; i++)
		{
			fa[i][j]=fa[fa[i][j-1]][j-1];
		}
	}
	a.assign(n+1,vector<int>());
	tr.assign(n+1,seg());
	for(int i=1; i<=m; i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		int l=lca(x,y);
		a[x].push_back(z);
		a[y].push_back(z);
		a[l].push_back(-z);
		a[fa[l][0]].push_back(-z);
	}
	ans.assign(n+1,0);
	dfs2(1);
	for(int i=1; i<=n; i++)
	{
		cout<<ans[i]<<"\n";
	}
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cout<<fixed<<setprecision(12);
	int T=1;
//	cin>>T;
	while(T--)
	{
		O_o();
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2139033.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

松理解数据库并发调度与可串行性

‍ 前言 在数据库系统中&#xff0c;多个事务的并发执行是不可避免的。然而&#xff0c;并发执行可能导致数据不一致的情况。为了解决这个问题&#xff0c;数据库管理系统&#xff08;DBMS&#xff09;使用调度策略来控制事务的执行顺序。本文将简洁地介绍可串行化调度这一概…

基于springboot旅游管理系统设计与实现

基于springboot旅游管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本旅游管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助使用…

[数据集][目标检测]智慧交通铁轨裂缝检测数据集VOC+YOLO格式4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2709 标注数量(xml文件个数)&#xff1a;2709 标注数量(txt文件个数)&#xff1a;2709 标注…

通过对比理解C++智能指针

理论 概述 智能指针&#xff1a;把管理资源的责任交给了对象&#xff0c;这样我们在使用结束时不需要显式地释放资源&#xff0c;而是由析构函数自动完成这一工作 它是一个类模板&#xff0c;可以创建任意类型的指针对象 智能指针使用时&#xff0c;资源对象不能被重复释放&a…

【CSS|第1期】网页设计的演变:从表格布局到Grid布局

日期&#xff1a;2024年9月9日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉在这里插入代码片得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对…

调用系统的录音设备提示:line with format PCM_SIGNED 16000.0 Hz

javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 16000.0 Hz, 8 bit, mono, 1 bytes/frame, not supported. 打开 设置->隐私->麦克风->允许应用访问你的麦克风 与 16000Hz没关系 与 16000Hz没关系 与 16000Hz没关系

【iOS】dismiss多级的方法

前言 上次笔者总结过push和pop推入和推出界面的方法&#xff0c;这里对于dismiss多级的方法进行一个总结&#xff0c;推入推出方法可以看看笔者这篇博客&#xff1a;【iOS】UI学习——界面切换 dismiss推出多级的原理 当我们使用pop推入新的界面的时候&#xff0c;连续pop推…

复杂情感识别系统

复杂情感识别系统&#xff08;CERS&#xff09;是一种先进的技术平台&#xff0c;旨在通过分析情感的组合、相互关系及其动态变化来解读和识别复杂的情感状态。这种系统通常采用以下技术和方法&#xff1a; 机器学习与深度学习&#xff1a; 通过训练算法识别和解释大量情感数据…

从汇编语言到高级语言:人类计算机科学的伟大探索

从20世纪中叶第一台电子计算机的诞生&#xff0c;到如今的智能设备遍布全球&#xff0c;计算机科学的发展历程是一部充满着人类探索精神的伟大史诗。计算机语言作为人类与机器交流的桥梁&#xff0c;见证并推动了这一切。从最早的汇编语言到如今多样的高级语言&#xff0c;我们…

视频监控摄像头国标GB28181配置参数逐条解析

转载&#xff1a;视频监控摄像头国标GB28181配置参数逐条解析 现在的很多信息化项目&#xff0c;都会涉及到国标GB28181的视频监控产品&#xff0c;当我们配置这些国标平台&#xff0c;录像机&#xff0c;摄像头时&#xff0c;如果对相关参数的定义不清楚的话&#xff0c;会给我…

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

Redis基础数据结构之 quicklist 和 listpack 源码解读

目录标题 quicklist为什么要设计 quicklist&#xff1f;quicklist特点ziplist quicklist数据结构 listpacklistpack是什么&#xff1f;listpack数据结构ziplist干啥去了&#xff1f;为什么有listpack?什么是ziplist的连锁更新&#xff1f;listpack 如何避免连锁更新&#xff1…

从ANN到SNN的转换:实现、原理及两种归一化方法【MINIST、实战】

从ANN到SNN的转换&#xff1a;实现、原理及两种归一化方法 引言 随着神经形态计算的迅猛发展&#xff0c;脉冲神经网络&#xff08;Spiking Neural Networks, SNNs&#xff09;作为一种仿生神经计算模型&#xff0c;逐渐展现出其在低功耗和事件驱动计算领域的巨大潜力。不同于…

8.5LoG算子边缘检测

LoG的基本概念 LoG&#xff08;Laplacian of Gaussian&#xff09;算子是一种结合了高斯模糊和平滑处理的边缘检测方法。它通过先对图像应用高斯滤波器来去除噪声&#xff0c;然后再对结果应用拉普拉斯算子来检测边缘。LoG算子的主要优点是可以检测图像中的边缘和其他重要特征…

MPICH 源码编译 with ucx with cuda,应用示例

先基于 cuda 编译ucx 再基于 ucx 编译 mpich mkdir mpich mkdir ucx 1, 安装 ucx 预备环境&#xff1a; sudo apt-get install valgrind sudo apt-get install libibverbs-dev librdmacm-dev 下载ucx 源代码 git clone --recursive https://github.com/openucx/ucx.git cd…

堆排序,快速排序

目录 1.堆排序 2.快速排序 1.hoare版本 2.挖坑法 3.前后指针法 注意点 1.堆排序 void Swap(int* a, int* b) {int tmp *a;*a *b;*b tmp; } void adjustdown(int* a, int n, int parent) {int child parent * 2 1;while (child < n){if (child 1 < n &&am…

【Python基础】Python lambda(简洁与高效的匿名函数)

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、lambda函数的基本概念三、lambda函数的应用实例3.1 在列表排序中使用lambda函数3.2 在map()函数中…

(批处理)设置延时+设置关机倒计时

使用方式&#xff1a;建立一个文本文件夹&#xff0c;将文件扩展名改为.bat&#xff0c;右键单击后编辑&#xff0c;将代码复制进去。 将文件保存 echo off echo 三秒后会出现一个提示自动关机ping -n 3 127.0.0.1 >nul rem 实现的功能是在这里停3秒再继续往下执行 rem 以…

OpenCore Legacy Patcher 2.0.0 发布,83 款不受支持的 Mac 机型将能运行最新的 macOS Sequoia

在不受支持的 Mac 上安装 macOS Sequoia (OpenCore Legacy Patcher v2.0.0) Install macOS on unsupported Macs 请访问原文链接&#xff1a;https://sysin.org/blog/install-macos-on-unsupported-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主…

【Linux】多路转接epoll

一、I/O多路转接 poll 1.1 poll函数接口 函数原型 函数参数 fds&#xff1a;是一个poll函数监听的结构列表&#xff0c;每一个元素中包含了三部分内容&#xff1a;文件描述符&#xff0c;监听的事件集合&#xff0c;返回的事件集合。nfds&#xff1a;表示的是fds数组的长度tim…