线段树合并:从入门到放弃


  • 感谢这篇博客(这里跳转)以及邱宇大神的讲解,我也算作入门(自闭)了。
  • 需要掌握的前置知识点:动态开点线段树、权值线段树。
一、合并思想

线段树合并,就是指建立一颗新的线段树,保存原有的两颗线段树的信息。

那么就是:

假设现在合并到了线段树的a、b某一个pos

如果a在这个区间有pos,b没有,那么新的线段树pos位置赋值为a

如果b在这个区间有pos,a没有,那么新的线段树pos位置赋值为b

如果a、b都有,那么继续

如果此时处理到了两颗线段树的叶子节点,就把b在pos的值加到a上,把新线段树上的pos位置赋值为a

递归处理左子树

递归处理右子树

用左右子树的值更新当前节点

将新线段树上的pos位置赋值为a,返回

二、复杂度证明
  • 这个我确实不太会,所以直接借用一下大佬博客的吧。(咕咕咕)
三、题目
  • 例题1:CF600E
  • 题目很简单,但应该是第一次尝试打合并,没有独立打完代码,而是看着题解代码打的。算作板子吧。
#include<bits/stdc++.h>
#define ll long long
#define lson tr[pos].l
#define rson tr[pos].r
using namespace std;
const int N=1e5+10;
const int inf=1e5;
int cnt,tot,n,a[N],rt[N],ver[N<<1],Next[N<<1],lin[N];ll anss[N];
struct tree{
	int sum,val,l,r;ll ans;
}tr[N*20];
void add(int x,int y){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;}
void maintain(int pos){
	if(tr[lson].sum>tr[rson].sum){
		tr[pos].val=tr[lson].val;
		tr[pos].sum=tr[lson].sum;
		tr[pos].ans=tr[lson].ans;
	}else if(tr[lson].sum<tr[rson].sum){
		tr[pos].val=tr[rson].val;
		tr[pos].sum=tr[rson].sum;
		tr[pos].ans=tr[rson].ans;
	}else{
		tr[pos].val=tr[lson].val;
		tr[pos].sum=tr[lson].sum;
		tr[pos].ans=tr[lson].ans+tr[rson].ans;
	}
	return;
}
int merge(int a,int b,int l,int r){
	if(!b||!a) return a+b;
	if(l==r){
		tr[a].val=l;
		tr[a].sum+=tr[b].sum;
		tr[a].ans=l;
		return a;
	}
	int mid=l+r>>1;
	tr[a].l=merge(tr[a].l,tr[b].l,l,mid);
	tr[a].r=merge(tr[a].r,tr[b].r,mid+1,r);
	maintain(a);
	return a;
}
void update(int &pos,int l,int r,int now,int v){
	if(!pos){
		pos=++cnt;
	}
	if(l==r){
		tr[pos].sum++;
		tr[pos].val=l;
		tr[pos].ans=l;
		return ;
	}
	int mid=l+r>>1;
	if(now<=mid) update(tr[pos].l,l,mid,now,v);
	else update(tr[pos].r,mid+1,r,now,v);
	maintain(pos);
}
void dfs(int x,int f){
	for(int i=lin[x];i;i=Next[i]){
		int y=ver[i];
		if(y==f) continue;
		dfs(y,x);
		merge(rt[x],rt[y],1,inf);
	}
	update(rt[x],1,inf,a[x],1);
	anss[x]=tr[rt[x]].ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]),rt[i]=++cnt;
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1,0);
	for(int i=1;i<=n;++i) printf("%I64d ",anss[i]);
	cout<<endl;
	return 0;
}

题目先咕着,有空写。

以上

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值