P3384 【模板】轻重链剖分----------------------------模板

在这里插入图片描述
在这里插入图片描述
概念:

重儿子:父亲节点的所有儿子中子树结点最多(size[x])的节点
轻儿子:父亲节点中除了重儿子以外的儿子
重边:父亲结点和重儿子连成的边
轻边:父亲节点和轻儿子连成的边
重链:由多条重边连接而成的路径
轻链:由多条轻边连接而成的路径
dep[x]:x的深度
size[x]:以x为根的子树节点个数总和
id[x]:dfs序所访问的编号
fa[x]:保存u的父亲节点
son[x]:保存重儿子
rk[x]:保存当前dfs序标号在树中所对应的节点
top[x]:保存重链的顶点

性质:

1,如果(u, v)是一条轻边,那么size(v) < size(u)/22,从根结点到任意结点的路所经过的轻重链的个数必定都小于logn;

3, 若求以x为根的子树和,那么子树节点的编号为[id[x],id[x]+size[x]-1]

4, 为何可以运用到线段树,因为dfs序使得重链或轻链上的序号都是有顺序的

时间复杂度:

树链剖分的时间复杂度为 O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1000; 
struct edge
{
	int next,to;
}e[N<<2];
struct  node
{
	int l,r,w,siz,lazy; 
}tr[N<<2];
int idx,root,MOD,cnt=0,n,m;
int a[N];
int dep[N],fa[N],son[N],size[N],top[N],id[N],rk[N];
int h[N];
//深度 ,父节点,重链儿子节点, ,重链的顶点,每个节点编号 ,编号对应的节点 
void add(int u,int v)
{
	e[++idx].to=v;e[idx].next=h[u];h[u]=idx;
 } 
void dfs1(int u,int father,int depth )//处理深度,父亲节点,u为根的节点个数 
 {
 	dep[u]=depth;fa[u]=father;size[u]=1;
 	int maxn=-1; 
 	for(int i=h[u];~i;i=e[i].next)
 	{
 		int j=e[i].to;
 		if(j==father) continue;
 		dfs1(j,u,depth+1);
 		size[u]+=size[j];
 		if(size[j]>maxn) //重链的儿子 
 		{
 			maxn=size[j];
 			son[u]=j;
		 }
	 }
 }
 void dfs2(int u,int v)
 {
 	top[u]=v;//标记重链的顶点
	id[u]= ++cnt;//节点对应编号
	rk[cnt]=a[u];//编号对应节点建树的关键一员 
	if(!son[u]) return ;
	dfs2(son[u],v);
	for(int i=h[u];~i;i=e[i].next)
	{
		int j=e[i].to;
		if(j!=son[u]&&j!=fa[u]) dfs2(j,j);//非重链的顶点就是自己 
	 } 
 	
 }
 void push_up(int u)
 {
 	tr[u].w=(tr[u<<1].w+tr[u<<1|1].w+MOD)%MOD;
 }
 void build(int u,int l,int r)
 {
 	tr[u].l=l;tr[u].r=r;tr[u].siz=r-l+1;
 	if(l==r)
 	{
 		tr[u].w=rk[l];
 		return ;
 		
	 }
	 int mid=l+r>>1;
	 build(u<<1,l,mid);build(u<<1|1,mid+1,r);
	 push_up(u);
 }
 void push_down(int u)//
 {
 	if(tr[u].lazy)
 	{

 		tr[u<<1].w=(tr[u<<1].w+tr[u<<1].siz*tr[u].lazy)%MOD;
		tr[u<<1|1].w=(tr[u<<1|1].w+tr[u<<1|1].siz*tr[u].lazy)%MOD;
		tr[u<<1].lazy=(tr[u<<1].lazy+tr[u].lazy)%MOD;
		tr[u<<1|1].lazy=(tr[u<<1|1].lazy+tr[u].lazy)%MOD;
		tr[u].lazy=0;
	 }
 }
 void Interadd(int u,int l,int r,int val)//
 {
 	if(l<=tr[u].l&&tr[u].r<=r)
 	{
 		tr[u].w+=tr[u].siz*val;
 		tr[u].lazy+=val;
 		return ;
	 }
	 push_down(u);
	 int mid=tr[u].l+tr[u].r>>1;
	 if(l<=mid) Interadd(u<<1,l,r,val);
	 if(r>mid) Interadd(u<<1|1,l,r,val);
	 push_up(u);
  } 
 void treeadd(int x,int y,int val)//
 {
 	while(top[x]!=top[y])//将两个节点跳到同一重链上 
 	{
 		if(dep[top[x]]<dep[top[y]]) swap(x,y);
 		Interadd(1,id[top[x]],id[x],val);
 		x=fa[top[x]];
	 }
	 if(dep[x]>dep[y]) swap(x,y);//节点编号的顺序依照深度大小,深度越大节点编号越大 
	 Interadd(1,id[x],id[y],val);//因为遍历线段树区间[l,r]必须从小到大 
 }
 int Insum(int u,int l,int r)//
 {
 	int ans=0;
 	if(l<=tr[u].l&&tr[u].r<=r) return tr[u].w;
	push_down(u);
 	int mid=tr[u].l+tr[u].r>>1;
 	if(l<=mid) ans=(ans+Insum(u<<1,l,r))%MOD;
    if(r>mid) ans=( ans+Insum(u<<1|1,l,r))%MOD;
	return ans;
 }
 void querysum(int x,int y)//
 {
 	int ans=0;
 	while(top[x]!=top[y])
 	{
 		if(dep[top[x]]<dep[top[y]]) swap(x,y);
 		ans=(ans+Insum(1,id[top[x]],id[x]))%MOD;
 		x=fa[top[x]];
	 }
	 if(dep[x]>dep[y]) swap(x,y);
	 ans=(ans+Insum(1,id[x],id[y]))%MOD;
	 printf("%d\n",ans);
 }
int main()
{
	memset(h,-1,sizeof h);
	scanf("%d %d %d %d",&n,&m,&root,&MOD);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs1(root,0,1);
	dfs2(root,root); 
	build(1,1,n); 
	while(m--)
	{
		int op,x,y,z;
		cin>>op;
		if(op==1)
		{
			scanf("%d %d %d",&x,&y,&z); z=z%MOD;
			treeadd(x,y,z);
		}
		else if(op==2)
		{
			scanf("%d %d",&x,&y);
			querysum(x,y);
		}
		else if(op==3)
		{
			scanf("%d %d",&x,&y);
			Interadd(1,id[x],size[x]+id[x]-1,y%MOD);
		}
		else if(op==4)
		{
			scanf("%d",&x);
			printf("%d\n",Insum(1,id[x],id[x]+size[x]-1));
		}
	}
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值