【学习笔记】线段树合并

前言

一般来说,线段树会有 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. 右儿子类似

核心代码

cpp 复制代码
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)

代码

cpp 复制代码
#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();
	}
}
相关推荐
handsome2135 小时前
WSL中使用GPU加速AMBER MD--测试
笔记·学习
WZF-Sang6 小时前
Linux权限理解【Shell的理解】【linux权限的概念、管理、切换】【粘滞位理解】
linux·运维·服务器·开发语言·学习
狂飙的张兴发6 小时前
认知小文2《成功之路:习惯、学习与实践》
学习·考研·职场和发展·跳槽·学习方法·改行学it·高考
爱编程的小新☆6 小时前
C语言内存函数
c语言·开发语言·学习
夜清寒风7 小时前
opencv学习:图像掩码处理和直方图分析及完整代码
人工智能·opencv·学习·算法·机器学习·计算机视觉
吃着火锅x唱着歌8 小时前
Go语言设计与实现 学习笔记 第七章 内存管理(1)
笔记·学习·golang
~在杰难逃~8 小时前
关于订单信息的Excel数据分析报告
笔记·数据分析·excel·数据分析报告
我命由我123459 小时前
2.使用 VSCode 过程中的英语积累 - Edit 菜单(每一次重点积累 5 个单词)
前端·javascript·ide·vscode·学习·编辑器·学习方法
Pluses9 小时前
Datawhale X 李宏毅苹果书 AI夏令营 《深度学习详解》第十九章 ChatGPT
人工智能·笔记·深度学习·学习
南山939 小时前
如何快速学习拼音打字?
学习·大学生·打字侠