【题解】P4556 雨天的尾巴

题面

题目传送门

前言

试图口述讲解但失败的云落

正文

前置芝士:线段树合并

这个是相对来说比较好理解的

对于待合并的两棵线段树,我们递归性地将每个节点合并

比如这道题我们需要求某一个最值对应的种类

所以比较容易想到,用线段树维护四个信息

  1. 左子树编号 l

  2. 右子树编号 r

  3. 多种救济粮的最大值 sum

  4. 救济粮的种类 col

然后我们动态开点建树

但是这是一个树上的信息维护

并且是路径修改

经典 trick:树上差分

对于每次修改 (u,v),我们直接在 u,v,lca(u,v),falca(u,v 上做修改操作

这个过程用线段树维护

当我们把所有的操作(或者说所有相关修改)都上了线段树组之后,考虑合并信息

合并的过程如下:设待合并的两棵线段树根节点为 x,y

x,y 开始合并

如果两个结点存在空结点,直接保留有结点的那一个即可

然后如果区间变为单点(即 l=r),直接将最大信息合并即可

否则就继续向下递归调用

遍历左子树,右子树,最后将答案上传

Warning: 线段树的合并函数的返回值为 int

为什么捏?返回的是什么?

可以参考 Treap 的合并

当我们把两棵线段树合并之后,我们会有一个新的根结点编号

这个根结点编号用于解决新线段树被调用的情况

比如说这个线段树作为一个新的待合并的树出现,那么根结点的编号就需要被调用

所以 merge 的返回值是 int

回顾一下整个过程:

  1. 输入,连边

  2. 倍增 LCA

  3. 树上差分,线段树维护单点修改

  4. 修改结束,将树形的线段树组做信息合并

  5. 最后统计答案

补充:

  • 线段树合并的数组空间大小为 O(m×logV),其中 m 是操作次数(本题中为 4m),V 是结点数

  • pushup 中不能简单用 max 修改 sum 的值,还需要连带修改 col 的信息,所以直接 if 判断

代码

#include<iostream>
#define endl '\n'
#define int long long
using namespace std;
const int maxn=1e5+10;
int n,m;
int head[maxn],tot;
struct Edge{
    int to,nxt;
}e[maxn<<1];
int fa[maxn][20],dep[maxn];
int rt[maxn],cnt;
int ans[maxn];
struct Segment_tree{
	struct node{
		int l,r,sum,col;
	}tr[maxn*50];
	void pushup(int u){
		if(tr[tr[u].l].sum>=tr[tr[u].r].sum){
			tr[u].sum=tr[tr[u].l].sum;
			tr[u].col=tr[tr[u].l].col;
		}else{
			tr[u].sum=tr[tr[u].r].sum;
			tr[u].col=tr[tr[u].r].col;
		}
		return;
	}
	void modify(int &u,int l,int r,int pos,int k){
		if(u==0){
			u=++cnt;
		}
		if(l==r){
			tr[u].sum+=k;
			tr[u].col=pos;
			return;
		}
		int mid=l+r>>1;
		if(pos<=mid){
			modify(tr[u].l,l,mid,pos,k);
		}else{
			modify(tr[u].r,mid+1,r,pos,k);
		}
		pushup(u);
		return;
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y){
			return x+y;
		}
		if(l==r){
			tr[x].sum+=tr[y].sum;
			return x;
		}
		int mid=l+r>>1;
		tr[x].l=merge(tr[x].l,tr[y].l,l,mid);
		tr[x].r=merge(tr[x].r,tr[y].r,mid+1,r);
		pushup(x);
		return x;
	}
}Tr;
inline void add(int u,int v){
    e[++tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot;
    return;
}
inline void dfs(int u,int fath){
	dep[u]=dep[fath]+1;
	fa[u][0]=fath;
	for(int i=1;i<=18;i++){
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
		if(v==fath){
			continue;
		}
		dfs(v,u);
	}
	return;
}
inline int lca(int x,int y){
	if(dep[x]<dep[y]){
		swap(x,y);
	}
	for(int i=18;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y]){
			x=fa[x][i];
		}
	}
	if(x==y){
		return y;
	}
	for(int i=18;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}

inline void solve(int u,int fath){
	for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
		if(v==fath){
			continue;
		}
		solve(v,u);
		rt[u]=Tr.merge(rt[u],rt[v],1,maxn);
	}
	ans[u]=(Tr.tr[rt[u]].sum?Tr.tr[rt[u]].col:0);
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
        add(u,v);
        add(v,u);
	}
	dfs(1,0);
	while(m--){
		int x,y,z;
		cin>>x>>y>>z;
		Tr.modify(rt[x],1,maxn,z,1);
		Tr.modify(rt[y],1,maxn,z,1);
		Tr.modify(rt[lca(x,y)],1,maxn,z,-1);
		Tr.modify(rt[fa[lca(x,y)][0]],1,maxn,z,-1);
	}
	solve(1,0);
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<endl;
	}
	return 0;
}

后记

完结撒花!

posted @   sunxuhetai  阅读(6)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示