首页 > 其他分享 >雨天的尾巴(P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并)

雨天的尾巴(P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并)

时间:2024-05-11 14:53:14浏览次数:13  
标签:rt int P4556 线段 尾巴 tree num root 雨天

1.题意简化

N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。

2.思路

  • 首先这道题肯定要用先建树,然后我们可以在树上的每个节点建一个权值线段树,考虑到空间问题(每个点都有1个权值为1e9的树),我们采用动态开点,在求解答案时我们再合并线段树

  • 对于x到y的路径上发放物品可以转化为4个步骤:
    1.从x根节点发放1次物品
    2.从y根节点发放1次物品
    3.设lca=x,y的最近公共祖先,我们从lca根节点撤回1次物品
    4.从lca父节点到根节点撤回1次物品、
    (可以自己手推一下)

  • 对于权值线段树上的每个节点结构体中存储4个数据左儿子编号右儿子编号,该区间最多物品的编号(w),该区间最多物品的数量(num)(也可以不用结构体,单独开数组存),在每次合并时比较两区间num大小,并更改,
    最终每个点的答案就是其所对应权值线段树根节点的w

  • 发放物品:每次发放的4个操作分别只在权值线段树上update一次(这样我们每次合并后其父节点相当于都是也update过了的),求答案是从上往下dfs递归去求,如:

void dfs2(int x)
{
	for (int i=he[x];i;i=ne[i])
	  if (to[i]!=f[x][0])
	  {
	  	dfs2(to[i]);
	  	root[x]=merge(root[x],root[to[i]],1,maxn);
	  }
	if (tree[root[x]].num)
	  ans[x]=tree[root[x]].w;
}


3.注意

  • 空间问题:m为动态开点的次数,每次插入新增log(n)个节点,n为值域
    我们每次发放物品对应4个操作所以权值线段树的空间可以开1e5*80

  • 这里是swap(x,y)不是(d[x],d[y]):

	if (d[x]>d[y]) swap(x,y);
  • 权值线段树叶子节点(l==r),l就为该点的w:
	if (l==r) 
	{
		tree[rt].w=l;
		tree[rt].num+=val;
		return;
	}
  • 在权值线段树上左区间的值域肯定要小于右区间,这样就不用再比较w的大小了:
void pushup(int rt)
{
	if (tree[lson].num>=tree[rson].num)
	{
		tree[rt].num=tree[lson].num;
		tree[rt].w=tree[lson].w;
		return;
	 } 
	else
	{
		tree[rt].num=tree[rson].num;
		tree[rt].w=tree[rson].w;
		return;
	}
}
  • 由于加减的问题可能会出现w不为0,但num为0的情况,所以要加一个判断:
	if (tree[root[x]].num)
	  ans[x]=tree[root[x]].w;

最后就是完整代码啦(开心):

#include<bits/stdc++.h>
#define lson tree[rt].ls
#define rson tree[rt].rs
using namespace std;
const int maxn=1e5+10;
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
struct node{
	int ls,rs;
	int num;//num为最大物品的数量
	int w;//w为最大物品的编号 
}tree[maxn*80];
int d[maxn],f[maxn][30];//lca需要的数组,d表示深度,f为倍增 
int he[maxn],ne[maxn*2],to[maxn*2],tot;//链式前向星存图 
int ans[maxn],root[maxn];//ans存答案,root记录根节点 
int n,m,tmp;
void addedge(int u,int v)
{
	ne[++tot]=he[u];
	he[u]=tot;
	to[tot]=v;
}
bool v[maxn];
void dfs1(int x,int fa)//为lca做预处理操作 
{
	v[x]=1;
	d[x]=d[fa]+1;
	for (int j=1;(1<<j)<=n;j++)
	  f[x][j]=f[f[x][j-1]][j-1];
	for (int i=he[x];i;i=ne[i])
	  if (!v[to[i]]) 
	    {
	    	f[to[i]][0]=x;
	    	dfs1(to[i],x);
	    }
}
int lca(int x,int y)//求最近公共祖先 
{
	if (d[x]>d[y]) swap(x,y);
	int k=log2(d[y]);
	for (int j=k;j>=0;j--)
	  if (d[f[y][j]]>=d[x]) 
	    y=f[y][j];
	if (x==y) return x;
	k=log2(d[y]);
	for (int j=k;j>=0;j--)
	  if (f[x][j]!=f[y][j]) 
	  {
	  	x=f[x][j];
	  	y=f[y][j];
	  }
	return f[x][0];
}
void pushup(int rt)
{
	if (tree[lson].num>=tree[rson].num)
	{
		tree[rt].num=tree[lson].num;
		tree[rt].w=tree[lson].w;
		return;
	 } 
	else
	{
		tree[rt].num=tree[rson].num;
		tree[rt].w=tree[rson].w;
		return;
	}
}
void update(int &rt,int l,int r,int pos,int val)//更新操作 
{
	if (!rt) rt=++tmp;
	if (l==r) 
	{
		tree[rt].w=l;
		tree[rt].num+=val;
		return;
	}
	int mid=(l+r)>>1;
	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;
	if (l==r) 
	{
		tree[ra].num+=tree[rb].num;
		return ra;
	}
	int mid=(l+r)>>1;
	tree[ra].ls=merge(tree[ra].ls,tree[rb].ls,l,mid);
	tree[ra].rs=merge(tree[ra].rs,tree[rb].rs,mid+1,r);
	pushup(ra);
	return ra;
}
void dfs2(int x)//合并权值线段树,求解答案 
{
	for (int i=he[x];i;i=ne[i])
	  if (to[i]!=f[x][0])
	  {
	  	dfs2(to[i]);
	  	root[x]=merge(root[x],root[to[i]],1,maxn);
	  }
	if (tree[root[x]].num)
	  ans[x]=tree[root[x]].w;
}
int main()
{
	n=read();
	m=read();
	for (int i=1,a,b;i<n;i++) 
	{
		a=read();
		b=read();
		addedge(a,b);
		addedge(b,a); 
	}
	dfs1(1,0);
	for (int i=1,x,y,p;i<=m;i++)
	{
		x=read();
		y=read();
		p=read();
		int t=lca(x,y); 
		update(root[x],1,maxn,p,1);//从 x 到 根节点 发放1次物品 
		update(root[y],1,maxn,p,1);//从 y 到 根节点 发放1次物品
		update(root[t],1,maxn,p,-1);//从 lca 到 根节点 撤回1次物品
		update(root[f[t][0]],1,maxn,p,-1);//从 lca的父节点 到 根节点 撤回1次物品
	}
	dfs2(1);
	for (int i=1;i<=n;i++)
	  printf("%d\n",ans[i]);
	return 0;
 } 

完结撒花!o( ̄▽ ̄)ブ

标签:rt,int,P4556,线段,尾巴,tree,num,root,雨天
From: https://www.cnblogs.com/zhagnxinyue/p/18184950

相关文章

  • 见鬼了!我家的 WiFi 只有下雨天才能正常使用...
    这是作者大学时期在家里遇到的一个非常奇怪的网络问题,作者的父亲是一名经验丰富的网络工程师,他们家里使用了一个复杂的网络设置,通过Wi-Fi桥接的方式,将父亲公司的高速商业网络连接到家中。但是有一天,作者发现家里的Wi-Fi只有在下雨时才能正常工作。。。事情发生在十多年前,那......
  • P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
    P4556[Vani有约会]雨天的尾巴/【模板】线段树合并在这题里面讲一下线段树合并。顾名思义就是把多个线段树合并成一个。显然完全二叉线段树(也就是普通线段树)是无法更高效的合并的,只能把所有节点加起来建个新树。但是在动态开点线段树中,有时候一个树只有几条链,这时候我们就是可......
  • 为何4G监控设备接入LiteCVR后,阴雨天气频繁出现播放卡顿现象?
    近年来,随着计算机、网络、图像处理以及传输技术的飞速发展,视频监控业务正在向其他领域加速渗透。LiteCVR视频融合平台基于云边端一体化架构,具有强大的数据接入、处理及分发能力,平台支持海量视频汇聚管理,可支持多协议、多类型的设备接入,属于性能稳定、高可靠、高可用的流媒体视频......
  • 题解 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    传送门如题目所言,这就是个线段树合并的板子题。题目大意题目描述首先村落里的一共有\(n\)座房屋,并形成一个树状结构。然后救济粮分\(m\)次发放,每次选择两个房屋\((x,y)\),然后对于\(x\)到\(y\)的路径上(含\(x\)和\(y\))每座房子里发放一袋\(z\)类型的救济粮。然......
  • Luogu P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    [Vani有约会]雨天的尾巴/【模板】线段树合并题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无奈......
  • 连续下雨天,.net开发者如何预防流感
    最近连续下了3天雨,天气变化大,很容易引发感冒咳嗽等疾病。对于.NET技术开发人员来说,如何保持身体健康,保证工作效率是一个很重要的问题。首先,我们需要注意保持室内空气流通,避免长时间处于封闭的空间。在开发过程中,我们通常需要长时间坐在电脑前,容易导致眼睛疲劳和颈椎病等问题。因......
  • 我抓住了 小尾巴
    继上次:"我抓住了腾讯的小尾巴"再次发表此文章,眼见为实我估计是开发的人太忙了,有时候根本不管有没有评论...    ......
  • 仿QQ尾巴,让程序给记事本输入文字!:)
    效果截图如下:程序主要代码如下:voidCSendMessageNotepadDlg::OnBnClickedBtnsend()//发送按钮单击处理函数{HWNDhParent=NULL,hChild=NULL;hParent=::FindWindow(T......
  • 雨天与心情
    说到“雨天”,不自觉地将之与“交通拥堵”、“出行不便”、“湿了鞋子”、“阴冷潮湿”联想到了一起,对我来说,我还联想到“骑自行车湿了背包”。这样一想,不喜欢下雨......
  • ORM的一些尾巴和Ajax的基础
    今日内容详细Q查询进阶操作使用Q查询记得先导入fromdjango.db.modelsimportQ#1.先产生Q对象q_obj=Q()#2.默认多个条件的连接条件是and可以修改为orq_obj.c......