线段树合并(雨天的尾巴 题解)

线段树合并

前置知识:权值线段树,动态开点;
其实动态开点就是当前节点的左右子区间的节点编号不是单纯的2倍和2倍加1的关系;
我们需要存储一下左右子区间的节点编号,就可以了;
线段树合并,顾名思义,就是,将两颗值域相同权值线段树合并为一棵线段树,并且对于相同节点
相加的一些操作,具体在代码中讲解;
模板题
雨天的尾巴

#include<iostream>
#include<cstdio>
#define l(o) (t[o].ls)
#define r(o) (t[o].rs)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+7;
const int M=1e5;
struct edge{
	int v,nxt;
}e[N<<1];
struct Setment{
	int ls,rs,dat,id;
}t[N*20*4];//注意线段树大小是不确定的,一般开这么多
int n,m,cnt,tot;
int head[N],dep[N],top[N],siz[N],hs[N],fa[N],ans[N],rt[N];
void add_edge(int u,int v){
	e[++cnt]=(edge){v,head[u]};
	head[u]=cnt;
}
void get_tree(int u){
	dep[u]=dep[fa[u]]+1;
	siz[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int to=e[i].v;
		if(to!=fa[u]){
			fa[to]=u;
			get_tree(to);
			siz[u]+=siz[to];
			if(siz[hs[u]]<siz[to]) hs[u]=to;
		}
	}
}
void dfs(int u,int fat){
	top[u]=fat;
	if(hs[u]) dfs(hs[u],fat);
	for(int i=head[u];i;i=e[i].nxt){
		int to=e[i].v;
		if(to!=fa[u]&&to!=hs[u]){
			dfs(to,to);
		}
	}
}

int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

//以上为求lca操作
void up(int o){
	t[o].dat=max(t[l(o)].dat,t[r(o)].dat);
	t[o].id=t[o].dat==t[l(o)].dat?t[l(o)].id:t[r(o)].id;//尽量选小的
}

void insert(int &o,int l,int r,int k,int val){
	if(!o) o=++tot;//动态开点,注意这里的取地址符号,是为了把编号存下来
	if(l==r){
		t[o].dat+=val;
		t[o].id=k;
		return;
	}
	if(k<=mid) insert(l(o),l,mid,k,val);
	if(k>mid) insert(r(o),mid+1,r,k,val);
	up(o);
}


void merge(int &x,int y,int l,int r){
	if(!x) {
		x=y;
		return;
	}
	if(!y) return;
	if(l==r){
		t[x].dat+=t[y].dat;
		return;
	}
	merge(t[x].ls,t[y].ls,l,mid);
	merge(t[x].rs,t[y].rs,mid+1,r);
	up(x);
}

void get_ans(int u){
	for(int i=head[u];i;i=e[i].nxt){
		int to=e[i].v;
		if(to!=fa[u]){
			get_ans(to);
			merge(rt[u],rt[to],1,M);
		}
	}
	ans[u]=t[rt[u]].id;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add_edge(x,y);
		add_edge(y,x);
	}
	get_tree(1);
	dfs(1,1);
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
//		cout<<"ffff"<<"\n";
		int ff=lca(x,y);
//		cout<<"kkkkk"<<"\n";
		insert(rt[x],0,M,z,1);//rt[]数组表示为以x为根节点的线段树的最大区间;
		insert(rt[y],0,M,z,1);
		insert(rt[ff],0,M,z,-1);
		insert(rt[fa[ff]],0,M,z,-1);//点差分
	}
	get_ans(1);
	for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
}
/*
10 10 
2 1 
3 2 
4 3 
5 3 
6 3 
7 4 
8 5
9 8 
10 3 
6 2 6 
3 2 6 
3 3 2 
6 6 6 
10 3 3 
10 7 1 
3 7 4 
7 9 5 
4 2 4 
3 3 6

0
6
6
4
5
6
1
5
5
1
*/
posted @ 2020-10-22 10:42  Aswert  阅读(116)  评论(0编辑  收藏  举报