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

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

时间:2024-07-22 15:43:29浏览次数:11  
标签:int 线段 tr leq Vani 救济粮 now 模板

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

题目背景

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

题目描述

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

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

输入格式

输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 \(n\) 和救济粮发放的次数 \(m\)。

第 \(2\) 到 第 \(n\) 行,每行有两个用空格隔开的整数 \(a, b\),代表存在一条连接房屋 \(a\) 和 \(b\) 的边。

第 \((n + 1)\) 到第 \((n + m)\) 行,每行有三个用空格隔开的整数 \(x, y, z\),代表一次救济粮的发放是从 \(x\) 到 \(y\) 路径上的每栋房子发放了一袋 \(z\) 类型的救济粮。

输出格式

输出 \(n\) 行,每行一个整数,第 \(i\) 行的整数代表 \(i\) 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。

如果某座房屋没有救济粮,则输出 \(0\)。

样例 #1

样例输入 #1

5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3

样例输出 #1

2
3
3
0
2

提示

  • 对于 \(20\%\) 的数据,保证 \(n, m \leq 100\)。
  • 对于 \(50\%\) 的数据,保证 \(n, m \leq 2 \times 10^3\)。
  • 对于 \(100\%\) 测试数据,保证 \(1 \leq n, m \leq 10^5\),\(1 \leq a,b,x,y \leq n\),\(1 \leq z \leq 10^5\)。

链接

假设粮食的种类数量为\(k\)

树上的路径区间加法操作,很明显是树上差分,但是对于不同的种类,还需要分来计算,再统计最大值。
一个思路就是对每个点开一个数组,然后用lca完成区间加法,再对不同种类查询k次,总复杂度为\(O(nk+nlogn)\)
而\(k\)与\(n\)是同一个数量级的,自然是无法接受的。

这里需要用到线段树合并。
通过线段树合并来快速统合两个不同节点上的信息,并查询需要的答案。
这里面就是对每个点,将树上差分所产生的不同种类的\(+1\)和\(-1\)放入一个大小为\(k\)的线段树中,这个线段树只需要维护里面所有数字的最大值即可。而\(k\)次的树上累加来求差分值,则变成了从底部向上的线段树合并,合并的同时是可以保证线段树的结构来快速查询最大值的。

所以就没了
这里也非常体现了动态开点和线段树合并的优越性,一个大大降低了空间复杂度,很大程度上减少了多棵线段树时的无用点,一个则是在\(O(nlogn)\)的时间内统合了原本用\(O(nk)\)的时间才能够计算的东西,也就是合并了\(n\)个节点较少的线段树。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
struct edge
{
	int next,to;
}e[200001];
int head[200001],tot,dep[100001][21],f[100001][21],n,m,ans[100001],Right,st[100001];
struct Ask
{
	int x,y,z;
}ques[100001];
struct Segment_Tree
{
	int l,r,Max,Kind;
}tr[6000001];
int p;
inline void add(int i,int j)
{
	e[++tot].next=head[i];
	e[tot].to=j;
	head[i]=tot;
}
void dfs1(int x,int fa,int now)
{
//	cout<<x<<endl;
	f[x][0]=fa;dep[x][0]=now-1;
	for(int i=head[x];i!=0;i=e[i].next)
	{
		int u=e[i].to;
		if(u==fa)continue;
		dfs1(u,x,now+1);
	}
}
int Get_Lca(int x,int y)
{
	if(dep[x][0]<dep[y][0])swap(x,y);
	int now=20;
	while(now>=0)
	{
		if(dep[x][now]>dep[y][0])x=f[x][now];
		now--;
	}
	if(x==y)return x;
	now=20;
	while(now>=0)
	{
		if(f[x][now]!=f[y][now])x=f[x][now],y=f[y][now];
		now--;
	}
	return f[x][0];
}
inline void push_up(int x)
{
	if(tr[tr[x].l].Max>=tr[tr[x].r].Max)
	tr[x].Max=tr[tr[x].l].Max,tr[x].Kind=tr[tr[x].l].Kind;
	else 
	tr[x].Max=tr[tr[x].r].Max,tr[x].Kind=tr[tr[x].r].Kind;
}
int change(int now,int x,int y,int Ned,int val)
{
	if(now==0)now=++p;
	if(x==y)
	{
		tr[now].Max+=val;
		tr[now].Kind=x;
		return now;
	}
	int mid=x+y>>1;
	if(Ned>=mid+1)tr[now].r=change(tr[now].r,mid+1,y,Ned,val);
	else tr[now].l=change(tr[now].l,x,mid,Ned,val);
	push_up(now);
	return now;
}
bool flag;
int merge(int l,int r,int x,int y)
{
//	if(flag)cout<<x<<' '/*<<mid*/<<' '<<y<<endl;
	if(l==0)return r;if (r==0) return l;
	if(x==y)
	{
		tr[l].Max+=tr[r].Max;
		tr[l].Kind=x;
		return l;
	}
	int mid=x+y>>1;
	tr[l].l=merge(tr[l].l,tr[r].l,x,mid);
	tr[l].r=merge(tr[l].r,tr[r].r,mid+1,y);
	push_up(l);
	return l;
}
void dfs2(int x,int fa)
{
	for(int i=head[x];i!=0;i=e[i].next)
	{
		int u=e[i].to;
		if(u==fa)continue;
		dfs2(u,x);
//		if(x==1&&u==3)flag=1;
		st[x]=merge(st[x],st[u],1,Right);
//		if(x==1)flag=0;
//		push_up(st[x]);
	}
//	if(x==1)
//	{
//		cout<<tr[st[x]].Kind<<endl;
//	}
//	cout<<x<<' '<<tr[st[x]].Max<<' '<<tr[st[x]].Kind<<endl;
	if(tr[st[x]].Max!=0)ans[x]=tr[st[x]].Kind;
}
int main()
{
//	freopen("1.in","r",stdin);
	n=read();m=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	dfs1(1,0,1);
	for(int j=0;j<=19;j++)
	{
		for(int i=1;i<=n;i++)
		{
			f[i][j+1]=f[f[i][j]][j];
			dep[i][j+1]=dep[f[i][j]][j];
		}
	}
	for(int i=1;i<=m;i++)
	{
		ques[i].x=read(),ques[i].y=read(),ques[i].z=read();
		Right=max(Right,ques[i].z);
	}
	for(int i=1;i<=m;i++)
	{
		int Lca=Get_Lca(ques[i].x,ques[i].y);
//		cout<<Lca<<' ';
		st[ques[i].x]=change(st[ques[i].x],1,Right,ques[i].z,1);
		st[ques[i].y]=change(st[ques[i].y],1,Right,ques[i].z,1);
		st[Lca]=change(st[Lca],1,Right,ques[i].z,-1);
		if(f[Lca][0]!=0)st[f[Lca][0]]=change(st[f[Lca][0]],1,Right,ques[i].z,-1);
	}
	dfs2(1,0);
	for(int i=1;i<=n;i++)
	{
//		cout<<tr[st[i]].Max<<' ';
		cout<<ans[i]<<endl;
	}
	return 0;
}

线段树合并的使用要求就是查询的信息和修改的操作要满足分配律。而一般则是用来实现较大面积的信息结合。。有点抽象,因为我不知道怎么描述,没写过多少题,后面总结的就清晰了。

标签:int,线段,tr,leq,Vani,救济粮,now,模板
From: https://www.cnblogs.com/HLZZPawa/p/18316127

相关文章

  • 编辑距离与滚动数组优化 - 二维动态规划模板
    题目:编辑距离。思路显然,定义\(f[i][j]\)表示字符串\(a\)中前\(i\)个字符到字符串\(b\)中前\(j\)个字符的编辑距离。那么对于\(a_i=b_j\)时,我们对当前位无需进行任何编辑操作,则\(f[i][j]=f[i-1][j-1]\)。如果\(a_i\neb_j\),那么我们就要对当前位进行编辑:......
  • 易优CMS模板标签downloadpay下载付费
    [基础用法]标签:downloadpay描述:下载模型实现付费,会员专享,会员付费下载,在使用之前先频道模型->下载模型->编辑->开启下载付费使用方法:付费标签需要加载/template/pc/system/download_pay.htm模板文件下载地址:点击下载,该文件放在pc模板目录......
  • 易优CMS模板标签type指定栏目输出单页模型栏目的详细内容
    [基础用法]标签:type描述:获取指定栏目信息用法:{eyou:typetypeid='栏目ID'empty='暂时没有数据'}<ahref="{$field.typeurl}">{$field.typename}</a>{/eyou:type}属性:typeid=''指定栏目ID,如果没有指定则获取当前列表页的栏目IDtype='self'表示当前栏目ty......
  • 最长不降子序列 n log n 方案输出与 Dilworth 定理 - 动态规划模板
    朴素算法不必多说,\(O(n^2)\)的暴力dp转移。优化算法时间为\(O(n\logn)\),本质是贪心,不是dp。思路是维护一个单调栈(手写版),使这个栈单调不降。当该元素\(\ge\)栈顶元素时,把这个元素压入栈中。否则,在单调栈中找到第一个大于该元素的项,把这一项改为这个元素。(因为要......
  • 线段树优化建图一种编号方式的理解
    intid(intl,intr){return(l+r)|(l!=r);}//代码1证明思路:引导并说明某种做法发生冲突的情况,并证明修改后不会发生冲突首先让我们考虑如果为intid(intl,intr){return(l+r);}//代码2会出现什么冲突,如图此时[1,3]与[2,2],[1,5]与[3,3]冲突结论1:线段树中序......
  • 【搜索】【模板】 IDDFS
    IDDFS使用场景使用dfs由于状态量太大会TLE,bfs会MLE。答案必须很小,一般在20以内。算法原理设置dfs的搜索深度限制\(dep\),dfs穷举\(dep\)层的答案。若在\(dep\)层找到满足条件的情形,则\(dep\)则为答案,否则\(dep\leftarrowdep+1\),继续搜索。优......
  • 【搜索】【模板】记忆化搜索
    记忆化搜索思想是实现DP的一种手段。优点:不关心递推顺序;对于两维及以上的DP,方便处理初始状态和无效状态。缺点:无法使用滚动数组。注意事项:要什么状态搜什么状态;所有的状态转移都要采取直接搜索的数据很傻。越界的状态不能赋值。实现步骤:先判断是否越界,如果越......
  • 【数学】【模板】扩展欧几里得算法
    扩展欧几里得算法思想首先回忆一下裴蜀定理:对于任意一对不全为\(0\)的整数\(a,b\),存在\(x,y\)使得\(ax+by=\gcd(a,b)\)。扩展欧几里得算法就是求出一组解满足\(ax+by=\gcd(a,b)\),这里用到了欧几里得算法,不会的,可以看看我的博客。我们看看怎么求:当\(b=......
  • 【数学】【模板】欧拉定理
    欧拉定理思想若\(a\)与\(n\)互质,则\(a^{\varphi(n)}\equiv1\pmodn\);容易得出当\(n\)为质数时,\(a^{n-1}\equiv1\pmodn\)。证明假设与\(1\simn\)中与\(n\)互质的数字为\(x_1,x_2,x_3,\cdotsx_{\varphi(n)}\),且两两不同。现将它们全部乘以\(a\)得......
  • 【数学】【模板】欧拉函数
    欧拉函数思想\(\varphi(n)\)表示的是\(1\simn\)中与\(n\)互质的个数。怎么求\(\varphi(n)\)呢?先将\(n\)质因数分解:\(n=p_1^{a_1}p_2^{a_2}\cdotsp_k^{a_k}\),那么\(\varphi(n)=n\left(1-\dfrac{1}{p_1}\right)\left(1-\dfrac{1}{p_2}\right)\cdots\left......