【学习笔记】线段树合并

前言

一般来说,线段树会有 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();
	}
}
相关推荐
项目題供诗17 分钟前
ES语法学习
学习·elasticsearch·django
柒十三.40 分钟前
江科大51单片机笔记【10】蜂鸣器(上)
笔记·嵌入式硬件·51单片机
虾球xz1 小时前
游戏引擎学习第146天
学习·ffmpeg·游戏引擎
cape_NO_72 小时前
运动控制卡--概述学习
学习·自动化
飞向星河3 小时前
SV学习笔记——数组、队列
笔记·学习·c#
胡西风_foxww3 小时前
中学学习难点管理思维魔方
学习·中学·难点
北顾南栀倾寒3 小时前
[算法笔记]cin和getline的并用、如何区分两个数据对、C++中std::tuple类
笔记·算法
Moonnnn.4 小时前
51单片机——程序执行过程(手工汇编)
汇编·笔记·嵌入式硬件·学习·51单片机
zyhhsss4 小时前
大模型应用开发学习笔记
笔记
大宝剑1705 小时前
blender学习25.3.8
学习·blender