1. 程式人生 > 實用技巧 >【學習筆記】線段樹合併

【學習筆記】線段樹合併

具體地,線段樹合併是指合併兩棵動態開點權值線段樹。

模板

考慮合併過程,將樹\(x\)合併到\(y.\)

噹噹前位置兩棵樹中有一者為空的時候,可以直接繼承當前節點返回。

若到葉子的時候,直接合並資訊。

然後對當前樹\(x\)的左右葉子合併即可。

int merge(int x,int y,int l,int r){
	if(!x||!y)return x|y;  
	if(l==r){sum[x]+=sum[y];tag[x]=l;return x;}
	int mid=(l+r)>>1;
	ls[x]=merge(ls[x],ls[y],l,mid);
	rs[x]=merge(rs[x],rs[y],mid+1,r);
	pushup(x);return x;
}

對於程式碼中變數的解釋:

\(x\)是樹中節點的編號。\(sum[x]\)表示\(x\)點處答案的出現次數,\(tag[x]\)表示\(x\)處的答案。

答案記錄為出現次數最多的物品編號。

樹上更新資訊時,考慮左子樹優先(即左子樹大於等於)來保證編號儘量小。繼承的時候\(sum,tag\)要一起更新。

樹上修改的時候,由於是單點修改,所以到葉子的時候更新資訊,回來的時候\(pushup\)即可。

樹上差分:對於路徑\(<x,y>,\)我們考慮進行\(cnt[x]+1,cnt[y]+1,cnt[LCA]-1,cnt[fa[LCA]]-1.\)最後進行一次\(dfs\)合併答案。

\(LCA\)

程式碼中用了倍增。預處理了\(dep,pa,fa.\)

注意理解程式碼中的\(x,\)裡面是陣列版本的線段樹,所以函式中需要傳區間引數\([l,r].\)

注意理解程式碼中\(x,sum,tag\)的含義,是理解重點。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=6e6+10;
const int N=1e5+10;
int head[N],tot,cnt,n,m;
int ls[MAXN],rs[MAXN],f[N][21];
int dep[N],pa[N],ans[N];
struct E{int nxt,to;}e[N<<1];
inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;}
int sum[MAXN],tag[MAXN],rt[MAXN],R=100000;
void dfs(int x,int fa){
	pa[x]=f[x][0]=fa;
	dep[x]=dep[fa]+1;
	for(int i=1;i<=20;++i)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(j==fa)continue;
		dfs(j,x);
	}
}
inline void pushup(int x){
	if(sum[ls[x]]>=sum[rs[x]])sum[x]=sum[ls[x]],tag[x]=tag[ls[x]];
	else sum[x]=sum[rs[x]],tag[x]=tag[rs[x]];
}
int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;--i)
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;--i)
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
int change(int x,int l,int r,int pos,int v){
	if(!x)x=++cnt;
	if(l==r){
		sum[x]+=v;
		tag[x]=l;
		return x;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)ls[x]=change(ls[x],l,mid,pos,v);
	else rs[x]=change(rs[x],mid+1,r,pos,v);
	pushup(x);return x;
}
int merge(int x,int y,int l,int r){
	if(!x||!y)return x|y;  
	if(l==r){sum[x]+=sum[y];tag[x]=l;return x;}
	int mid=(l+r)>>1;
	ls[x]=merge(ls[x],ls[y],l,mid);
	rs[x]=merge(rs[x],rs[y],mid+1,r);
	pushup(x);return x;
}
void redfs(int x){
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(j==pa[x])continue;
		redfs(j);
		rt[x]=merge(rt[x],rt[j],1,R);
	}
	if(sum[rt[x]])ans[x]=tag[rt[x]];
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,x,y;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1,0);
	for(int i=1,x,y,z;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		int L=LCA(x,y);
		rt[x]=change(rt[x],1,R,z,1);
		rt[y]=change(rt[y],1,R,z,1);
		rt[L]=change(rt[L],1,R,z,-1);
		if(pa[L])rt[pa[L]]=change(rt[pa[L]],1,R,z,-1);
	}
	redfs(1);
	for(int i=1;i<=n;++i)printf("%d\n",ans[i]);
	return 0;
}