首页 > 其他分享 >雨天的尾巴(P4556 [Vani有约会] 雨天的尾巴)

雨天的尾巴(P4556 [Vani有约会] 雨天的尾巴)

时间:2024-05-11 20:20:33浏览次数:16  
标签:rt num int 尾巴 雨天 maxn ra Vani 节点

题目描述(简约版)

N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。
注释很全,请耐心看一看,写了好久哒

#include<bits/stdc++.h>
using namespace std;
#define lson t[rt].ls
#define rson t[rt].rs

const int maxn=1e5+10;
struct stu
{
	int ls,rs;//左右子树 
	int num;//物品个数的众数 
	int w;//个数最多的物品是哪种 
}t[maxn*80];
int n,m,f[maxn][30],dep[maxn];
int h[maxn],to[maxn*2],nxt[maxn*2],tot;
int root[maxn],segtot,ans[maxn];
bool vis[maxn];

void add(int x,int y)//加边 
{
	to[++tot]=y;
	nxt[tot]=h[x];
	h[x]=tot;
}

void dfs1(int x,int fa)
{
	vis[x]=true;//遍历过哩记录一下 
	dep[x]=dep[fa]+1;//比父节点深度+1 
	for(int j=1;(1<<j)<=n;j++)//倍增lca 
	{
		f[x][j]=f[f[x][j-1]][j-1];
	}
	for(int i=h[x];i;i=nxt[i])
	{
		if(!vis[to[i]])//根据刚刚的记录,如果遍历过了就跳过 
		//因为是双向边,所以需要判一下 
		{
			f[to[i]][0]=x;//子节点to[i]向上跳一个深度就是父节点 
			//0是2的0次方也就是1 
			dfs1(to[i],x);//继续遍历下一个 
		}
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);//反正只是求最近公共祖先,所以换一下也没关系
	//这样可以直接认为x深度就是比y小的,不用分两种情况了 
	int d=dep[x]-dep[y];//记录两个节点深度的差值,先跳到同一深度,再一起向上跳 
	for(int i=0;i<30;i++)
	{
		if(d&(1<<i)) x=f[x][i];//如果跳i深度还是比d小那就能跳,否则就不能跳了
		//否则就跳过了 
	}
	if(x==y) return x;//这里判的是如果正好一个节点是另一个的祖先 
	for(int i=29;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])//这里只能判!=,要是==的话,可能不是最近公共祖先
		//可能会跳过因为是从最大开始的 
		{
			x=f[x][i];//都同时向上跳一样的深度 
			y=f[y][i];
		}
		if(f[x][0]==f[y][0]) return f[x][0];//如果两个不同子节点的父节点相同 
		//说明已经到了要求的父节点了直接就能结束循环了 
	}
}

void pushup(int rt)//向上更新 
{
	if(t[lson].num>=t[rson].num)//这就很显而易见了吧,哪个大就取哪个子树的信息喽 
	{
		t[rt].num=t[lson].num;//把num和w全都附上最大值 
		t[rt].w=t[lson].w;
	}
	else
	{
		t[rt].num=t[rson].num;
		t[rt].w=t[rson].w;
	}
}

void update(int &rt,int l,int r,int pos,int val)//这里解释一下l和r虽然代指区间 
//但是实际就是物品种类,后面会具体体现,pos代表物品种类,val直接就是1 
//相当于这种物品在这个权值线段树中加1 
{
	if(!rt) rt=++segtot;//如果还没有以rt为根的子树,就新建一个叭 
	//这体现了动态开点的特点 
	if(l==r)//区间长度为1,表明已经递归到最底层可以开始赋值啦 
	{
		t[rt].w=l;//这里用l,r,pos都是一样的 
		t[rt].num+=val;//这种物品个数加1 
		return;//别忘了返回 
	}
	int mid=(l+r)>>1;//二分向下找pos 
	if(pos<=mid) update(lson,l,mid,pos,val);//这个就是二分查找了吧...? 
	else update(rson,mid+1,r,pos,val);
	pushup(rt);//向上更新众数和物品种类 
}

int merge(int ra,int rb,int l,int r)//合并 
{
	if(!ra||!rb) return ra|rb;//如果一个子树w物品为空就返回有值的一个 
	if(l==r)//如果找到w物品且都不为空 
	{
		t[ra].num+=t[rb].num;//将两个物品个数直接相加最后一定>=0 
		return ra;//返回 
	}
	int mid=(l+r)>>1;
	t[ra].ls=merge(t[ra].ls,t[rb].ls,l,mid);//二分向下更新子节点 
	t[ra].rs=merge(t[ra].rs,t[rb].rs,mid+1,r);
	pushup(ra);//向上更新父节点 
	return ra;
}

void dfs2(int x)//遍历求答案 
{
	vis[x]=true;
	for(int i=h[x];i;i=nxt[i])
	{
		if(!vis[to[i]])//又用到哩 
		{
			dfs2(to[i]);//不断往下遍历子节点 
			root[x]=merge(root[x],root[to[i]],1,maxn);//将子树的信息直接合并
			//到父节点上,不用考虑别的之前都处理好了 
		}
	}
	if(t[root[x]].num) ans[x]=t[root[x]].w;//如果num不为零就赋给ans啦 
}

int main()
{
	int x,y,z;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);//加双向边就不用担心有遍历不到的了,就可以随便取根啦 
		add(y,x);
	}
	dfs1(1,0);//因为是无向图,就随便取一个点当根就好啦,且认为他的父节点为0 
	memset(vis,false,sizeof(vis));//dfs2还要用先重置一下 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		int t=lca(x,y);//求最近公共祖先 
		update(root[x],1,maxn,z,1);//记录的时候只将x和y将w物品个数+1 
		update(root[y],1,maxn,z,1);//其他先不管,以后再向上合并 
		update(root[t],1,maxn,z,-1);//记录最近公共祖先w物品-1 
		//因为合并的时候x和yw都有1个1 
		update(root[f[t][0]],1,maxn,z,-1);//最近公共祖先的父节点再-1
		//合并的时候w本来没发在这个父节点,但合并的时候会合并上两个w
		//最近公共祖先减了一次,父节点本来第i次没发放w,所以要减两次 
		//应该能理解吧......语文不好见谅吧QAQ 
	}
	dfs2(1);//遍历一次求出每个点的众数 
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);//依次输出 
	
	return 0;
}

本代码借鉴于https://www.cnblogs.com/zhagnxinyue/p/18184950
注释全由本蒟蒻手打

标签:rt,num,int,尾巴,雨天,maxn,ra,Vani,节点
From: https://www.cnblogs.com/cuichenxi/p/18187098

相关文章

  • 雨天的尾巴
    [Vani有约会]雨天的尾巴/【模板】线段树合并题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无......
  • 雨天的尾巴(P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并)
    1.题意简化N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。2.思路首先这道题肯定要用先建树,然后我们可以在树上的每个节点建一个权值线段树,考虑到空间问题(每个点都有1个权值......
  • 见鬼了!我家的 WiFi 只有下雨天才能正常使用...
    这是作者大学时期在家里遇到的一个非常奇怪的网络问题,作者的父亲是一名经验丰富的网络工程师,他们家里使用了一个复杂的网络设置,通过Wi-Fi桥接的方式,将父亲公司的高速商业网络连接到家中。但是有一天,作者发现家里的Wi-Fi只有在下雨时才能正常工作。。。事情发生在十多年前,那......
  • P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
    P4556[Vani有约会]雨天的尾巴/【模板】线段树合并在这题里面讲一下线段树合并。顾名思义就是把多个线段树合并成一个。显然完全二叉线段树(也就是普通线段树)是无法更高效的合并的,只能把所有节点加起来建个新树。但是在动态开点线段树中,有时候一个树只有几条链,这时候我们就是可......
  • 体光伏效应和二次谐波产生的微观理论(Photogalvanic effect 、bulk photovoltaic effec
    此领域较易入门,经典文献为:1.综述:https://www.nature.com/articles/s41563-021-00992-72.Sipe大佬的论文:开创领域的两篇最经典论文,值得全部重复:https://journals.aps.org/prb/abstract/10.1103/PhysRevB.61.5337https://journals.aps.org/prb/abstract/10.1103/PhysRevB.52.146......
  • CF contest 1909 Pinely Round 3 (Div. 1 + Div. 2) 题解(Vanilla的掉分赛)
    CFcontest1909PinelyRound3(Div.1+Div.2)Vanilla的掉分赛绪言PinelyRound3(Div.1+Div.2)-Codeforces\[\color{purple}\large\textbf{世界上只有一种真正的英雄主义,}\]\[\color{red}\large\textbf{就是认清了生活的真相后还依然热爱它。}\]\[\color{gray}......
  • 为何4G监控设备接入LiteCVR后,阴雨天气频繁出现播放卡顿现象?
    近年来,随着计算机、网络、图像处理以及传输技术的飞速发展,视频监控业务正在向其他领域加速渗透。LiteCVR视频融合平台基于云边端一体化架构,具有强大的数据接入、处理及分发能力,平台支持海量视频汇聚管理,可支持多协议、多类型的设备接入,属于性能稳定、高可靠、高可用的流媒体视频......
  • 一款开源免费、更符合现代用户需求的论坛系统:vanilla
    对于个人建站来说,WordPress相信很多读者都知道了。但WordPress很多时候我们还是用来建立自主发布内容的站点为主,适用于个人博客、企业主站等。虽然有的主题可以把WordPress变为论坛,但效果并不是很好。所以,今天给大家推荐一个开源的论坛项目:vanilla,有建站需求的小伙伴可以关注一......
  • 《面试1v1》JavaNIO
    我是javapub,一名Markdown程序员从......
  • 题解 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    传送门如题目所言,这就是个线段树合并的板子题。题目大意题目描述首先村落里的一共有\(n\)座房屋,并形成一个树状结构。然后救济粮分\(m\)次发放,每次选择两个房屋\((x,y)\),然后对于\(x\)到\(y\)的路径上(含\(x\)和\(y\))每座房子里发放一袋\(z\)类型的救济粮。然......