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

题目描述

首先村落里的一共有 n 座房屋,并形成一个树状结构。然后救济粮分 m 次发放,每次选择两个房屋 (x, y),然后对于 x 到 y 的路径上(含 x 和 y)每座房子里发放一袋 z 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

解题思路

可以在每个节点用值域线段树维护最多的是那种救济粮,对于修改操作,可以运用树上差分的思想,在x和y增添一,在lca和lca的父亲处减掉一,最后查询时自底向上用线段树合并维护前缀和就可以了
PS:还有一个小细节是题目要求数量相同返回编号最小的,所以在pushup时

void pushup(int k)
{
	if(lson[k]==0)
	{
		Cnt[k]=Cnt[rson[k]];
		Max[k]=Max[rson[k]];
		return;
	}
	if(rson[k]==0)
	{
		Cnt[k]=Cnt[lson[k]];
		Max[k]=Max[lson[k]];
		return;
	}
	if(Cnt[lson[k]]>=Cnt[rson[k]])//一定要在这加上等号,因为是值域线段树,左儿子一定小于右儿子
	{
		Cnt[k]=Cnt[lson[k]];
		Max[k]=Max[lson[k]];
		return;
	}
	if(Cnt[lson[k]]<Cnt[rson[k]])
	{
		Cnt[k]=Cnt[rson[k]];
		Max[k]=Max[rson[k]];
		return;
	}
}
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
const int M = N*80;
const int R = 1e5+7;
struct node
{
	int y,next;
}e[2*N];
int link[N],t,n,m,tot=0;
void Add(int x,int y)
{
	t++;
	e[t].y=y;
	e[t].next=link[x];
	link[x]=t;
}
int fa[N],dep[N],top[N],siz[N],son[N];
void dfs(int x,int pre)
{
	fa[x]=pre;
	siz[x]=1;
	dep[x]=dep[pre]+1;
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==pre) continue;
		dfs(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])
		son[x]=y;
	}
}
void Exdfs(int x,int topth)
{
	top[x]=topth;
	if(!son[x]) return;
	Exdfs(son[x],topth);
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x]||y==son[x]) continue;
		Exdfs(y,y);
	}
}
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]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	return x;
}
int Max[M],Cnt[M],lson[M],rson[M],rot[N];
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
void pushup(int k)
{
	if(lson[k]==0)
	{
		Cnt[k]=Cnt[rson[k]];
		Max[k]=Max[rson[k]];
		return;
	}
	if(rson[k]==0)
	{
		Cnt[k]=Cnt[lson[k]];
		Max[k]=Max[lson[k]];
		return;
	}
	if(Cnt[lson[k]]>=Cnt[rson[k]])
	{
		Cnt[k]=Cnt[lson[k]];
		Max[k]=Max[lson[k]];
		return;
	}
	if(Cnt[lson[k]]<Cnt[rson[k]])
	{
		Cnt[k]=Cnt[rson[k]];
		Max[k]=Max[rson[k]];
		return;
	}
}
void update(int &k,int l,int r,int x,int add)
{
	if(!k) k=++tot;
	if(l==r)
	{
		Max[k]=l;
		Cnt[k]+=add;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(lson[k],l,mid,x,add);
	else update(rson[k],mid+1,r,x,add);
	pushup(k);
}
int ans[N];
void merge(int &x,int y,int l,int r,int root)
{
	if(!x) x=++tot;
	if(l==r)
	{
		Cnt[x]=Cnt[x]+Cnt[y];
		Max[x]=l;
		return;
	}
	int mid=(l+r)>>1;
	if(lson[x]&&lson[y]) merge(lson[x],lson[y],l,mid,root);
	else if(!lson[x]&&lson[y]) lson[x]=lson[y];
	if(rson[x]&&rson[y]) merge(rson[x],rson[y],mid+1,r,root);
	else if(!rson[x]&&rson[y]) rson[x]=rson[y];
	pushup(x);
}
void Get(int x)
{
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x]) continue;
		Get(y);
		merge(rot[x],rot[y],1,R,x);
	}
	ans[x]=Max[rot[x]];
	if(Cnt[rot[x]]==0) ans[x]=0;
}
int main()
{
	n=read();
	m=read();
	for(int i=1;i<n;i++)
	{
		int x,y;
		x=read();
		y=read();
		Add(x,y);
		Add(y,x);
	}
	dfs(1,0);
	Exdfs(1,1);
	while(m--)
	{
		int a,b,x;
		a=read();
		b=read();
		x=read();
		int lca=LCA(a,b);
		update(rot[a],1,R,x,1);
		update(rot[b],1,R,x,1); 
		update(rot[lca],1,R,x,-1);
		update(rot[fa[lca]],1,R,x,-1);
	}
	Get(1);
	for(int i=1;i<=n;i++)
	printf("%d\n",ans[i]);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值