【学习笔记】线段树合并

前言

一般来说,线段树会有 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();
	}
}
相关推荐
007php0071 小时前
某大厂跳动面试:计算机网络相关问题解析与总结
java·开发语言·学习·计算机网络·mysql·面试·职场和发展
知识分享小能手1 小时前
微信小程序入门学习教程,从入门到精通,微信小程序核心 API 详解与案例(13)
前端·javascript·学习·react.js·微信小程序·小程序·vue
递归不收敛1 小时前
吴恩达机器学习课程(PyTorch 适配)学习笔记:3.3 推荐系统全面解析
pytorch·学习·机器学习
又是忙碌的一天4 小时前
前端学习 JavaScript(2)
前端·javascript·学习
蒙奇D索大4 小时前
【数据结构】考研数据结构核心考点:二叉排序树(BST)全方位详解与代码实现
数据结构·笔记·学习·考研·算法·改行学it
玲娜贝儿--努力学习买大鸡腿版4 小时前
推荐算法学习笔记(十九)阿里SIM 模型
笔记·学习·推荐算法
光影少年4 小时前
Flutter生态及学习路线
学习·flutter
尤利乌斯.X4 小时前
复杂网络仿真从入门到精通:0 学习路线
网络·学习·matlab·仿真·复杂网络
梦幻精灵_cq5 小时前
70行代码展现我的“毕生”编程能力
学习
Yupureki5 小时前
从零开始的C++学习生活 6:string的入门使用
c语言·c++·学习·visual studio