【线段树】[LUOGU [Vani有约会]雨天的尾巴] 树上差分+线段树合并

题目:

题目链接:[LUOGU [Vani有约会]雨天的尾巴]
题解:
这个题,,,就是线段树合并的板子,加上一个树上差分操作即可。至于怎么树上差分,可以先想一下在序列中差分,因为这里是赋值的操作了,所以在序列中啊,就是在序列的首位出加一减一就可。这样的思想转化到树上对于树上的距离就是重在四个点上, x x x, y y y, l c a ( x , y ) lca(x,y) lca(x,y), f a [ l c a ( x , y ) ] fa[lca(x,y)] fa[lca(x,y)],主要是由于是在赋值为z,所以只对 x x x, y y y这两个点 + 1 +1 +1,在 l c a ( x , y ) lca(x,y) lca(x,y), f a [ l c a ( x , y ) ] fa[lca(x,y)] fa[lca(x,y)] − 1 -1 1即可。(可以自己手画个图理解理解)。之后就直接用权值线段树进行合并即可。

代码:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int sea=1e5+7;
struct hit{int ls,rs,max,sum,ans;}tr[sea*60];
struct see{int ver,next;}e[sea<<1];
int n,m,tot,seg,root[sea<<1],last[sea<<1],fa[22][sea<<1],deep[sea<<1];
void add(int x,int y){e[++tot].ver=y;e[tot].next=last[x];last[x]=tot;}
void DFS(int x,int f)
{
	fa[0][x]=f; deep[x]=deep[f]+1;
	for(int i=last[x];i;i=e[i].next)
	if(e[i].ver!=fa[0][x]) DFS(e[i].ver,x); 
}
int lca(int x,int y)
{
	if(deep[x]>deep[y]) swap(x,y);
	for(int d=deep[y]-deep[x],i=0;d;d>>=1,i++) if(d&1) y=fa[i][y];
	if(x==y) return x;
	for(int i=21;i>=0;--i) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
	return fa[0][x]; 
}
void up(int k)
{
	int l=tr[k].ls,r=tr[k].rs;
	if(tr[l].sum>=tr[r].sum) tr[k].max=tr[l].max,tr[k].sum=tr[l].sum;
	else tr[k].max=tr[r].max,tr[k].sum=tr[r].sum;
}
void build(int &k,int l,int r,int val,int tt)
{
	if(!k) k=++seg; 
	if(l==r) {tr[k].max=l,tr[k].sum+=tt;return ;}
	int mid=(l+r)/2;
	if(val<=mid) build(tr[k].ls,l,mid,val,tt);
	else build(tr[k].rs,mid+1,r,val,tt);
	up(k);
}
int incor(int x,int y,int l,int r)
{
	if(!x) return y; if(!y) return x;
	if(l==r){tr[x].sum+=tr[y].sum; return x;}
	int mid=(l+r)/2;
	tr[x].ls=incor(tr[x].ls,tr[y].ls,l,mid);
	tr[x].rs=incor(tr[x].rs,tr[y].rs,mid+1,r);
	up(x); return x;
}
void dfs(int x,int fa)
{
	for(int i=last[x];i;i=e[i].next)
	{
		int y=e[i].ver; if(y==fa) continue;
		dfs(y,x); root[x]=incor(root[x],root[y],1,sea);
	}
	if(tr[root[x]].sum) tr[x].ans=tr[root[x]].max;
}
int main()
{
	n=read(); m=read();
	for(int i=1,x,y;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
	seg=n;  deep[0]=0; DFS(1,0);
	for(int i=1;1<<i<=n;i++) for(int j=1;j<=n;j++)
	if(fa[i-1][j]<0) fa[i][j]=-1;else fa[i][j]=fa[i-1][fa[i-1][j]];
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),z=read(); int ins=lca(x,y);
		build(root[x],1,sea,z,1); build(root[y],1,sea,z,1);
		build(root[ins],1,sea,z,-1); build(root[fa[0][ins]],1,sea,z,-1);
	}
	dfs(1,0); for(int i=1;i<=n;i++) printf("%d\n",tr[i].ans);
	return 0;
} 

Continue……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值