[DS 小计] 线段树合并

引入

现在你有很多棵二叉树。
二叉树的节点总和是 \(n\)
现在,你要把它们合并。
怎么做呢?
实际上,写的好是可以 \(O(n)\) 完成的。

前置题目 1

给出 \(2\) 棵二叉树,合并两棵二叉树。

怎么做呢?
很容易的暴力,遍历每个点,合并即可。

合并我们进行以下分类讨论:

  • 如果现在 \(u\)\(v\) 中任意一左子树/右子树为空,直接接上去即可。
  • 否则,递归处理。

这样时间复杂度是重复节点个数。

好像没什么用的样子

前置题目 2

给出 \(n\) 棵二叉树,合并 \(n\) 棵二叉树。节点总数为 \(m\)

其实很简单,我们按照上面合并两棵的思路,暴力合并即可。
为什么这样可行呢?时间复杂度不会炸吗?

我们来分析时间复杂度。
那么,我们合并两棵线段树的时间复杂度是确定的。
我们可以理解为,合并后的两个节点,我们删除了其中一个。
也就是说,我们给其中一个打上了标记。
显然,每个节点至多被打上一次标记
所以时间复杂度是 \(O(m)\) 的。
复杂度的情况保证是不会重复合并两次二叉树。

正题

线段树合并和二叉树合并思想一致。
只是线段树要维护信息。
只需要这样做:
先下方当前节点的标记,然后往下递归合并。
这样我们能保证合并时的两个点没有标记,是正确的。
然后因为你已经下好标记了,然后更新左右子树,子树也是下方好标记/更新了的,直接上传标记(Pushup)即可。

所以,合并时间复杂度是总的点数。
这样就做完了。
具体的,到了叶子节点请特殊处理处理即可。

然后就做完了,完结撒花。

算法真正厉害的地方就是可以和 SAM 结合,后面会细🔒。

点击查看代码
#include<bits/stdc++.h>
#define N 100005
#define ll long long
#define ls(x) tr[x].r
#define rs(x) tr[x].l
using namespace std;
const int C=N-5;
int n,m;
struct tnode{
	int l,r,c,v;
};
int root[N],tot=1;
struct segtree{
	tnode tr[N*4*20];
	void Pushup(int x)
	{
		if(tr[ls(x)].v>=tr[rs(x)].v) 
			tr[x].c=tr[ls(x)].c,tr[x].v=tr[ls(x)].v;
		else tr[x].c=tr[rs(x)].c,tr[x].v=tr[rs(x)].v;
	}
	void modify(int &now,int l,int r,int p,int v)
	{
		if(!now) now=++tot;
		if(l==r)
		{
			tr[now].v+=v;
			tr[now].c=p;
			return;
		}
		int mid=(l+r)/2;
		if(p<=mid) modify(ls(now),l,mid,p,v);
		else modify(rs(now),mid+1,r,p,v);
		Pushup(now);
	}
	int merges(int u,int v,int l,int r)
	{
		if(!u) return v;
		if(!v) return u;
		if(l==r)
		{
			tr[u].v+=tr[v].v;
			return u;
		}
		int mid=(l+r)/2;
		ls(u)=merges(ls(u),ls(v),l,mid);
		rs(u)=merges(rs(u),rs(v),mid+1,r);
		Pushup(u);
		return u;
	}
}tr;
int head[N];
struct edge{
	int to,next;
}e[N*2];
void add(int u,int v)
{
	e[tot]=(edge){v,head[u]};
	head[u]=tot++;
}
int f[N][20],dep[N];
int glca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=18;i>=0;i--)
		if(dep[f[u][i]]>=dep[v]) u=f[u][i];
	if(u==v) return u;
	for(int i=18;i>=0;i--)
		if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i];
	return f[u][0];
}
void dfs1(int now,int fa)
{
	dep[now]=dep[fa]+1;
	f[now][0]=fa;
	for(int i=1;f[now][i-1];i++)
		f[now][i]=f[f[now][i-1]][i-1];
	for(int i=head[now];i;i=e[i].next)
	{
		int son=e[i].to;
		if(son==fa) continue;
		dfs1(son,now);
	}
}
int ans[N];
void dfs2(int now,int fa)
{
	for(int i=head[now];i;i=e[i].next)
	{
		int son=e[i].to;
		if(son==fa) continue;
		dfs2(son,now);
		root[now]=tr.merges(root[now],root[son],1,C);	
	}
	if(tr.tr[root[now]].v)ans[now]=tr.tr[root[now]].c;
}
int main() 
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs1(1,0);
	tot=0;
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		int lca=glca(u,v);
		tr.modify(root[u],1,C,w,1);
		tr.modify(root[v],1,C,w,1);
		tr.modify(root[lca],1,C,w,-1);
		tr.modify(root[f[lca][0]],1,C,w,-1);
	}
	dfs2(1,0);
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}
posted @ 2024-04-08 16:57  g1ove  阅读(7)  评论(0编辑  收藏  举报