树链剖分 模板小记

17 篇文章 0 订阅
7 篇文章 0 订阅

树 链 剖 分   模 板 小 记 树链剖分~模板小记  


Q . Q. Q. 树链剖分的背景?
A . A. A. 连通且无环的树
Q . Q. Q. 树链剖分的作用?
A . A. A. 大幅增加码量
A . A. A. 将一棵树变成几条链 树形变成线形 减少处理难度
Q . Q. Q. 树链剖分能处理哪些问题?
A 1 . A_1. A1. 修改的有:

  1. x x x节点 +   o r × v a l +~or\times val + or×val
  2. x x x y y y这条路径上所有节点 +   o r × v a l +~or\times val + or×val
  3. 将以 x x x为根的子树内所有节点 +   o r × v a l +~or\times val + or×val

A 2 . A_2. A2. 查询的有:

  1. 查询 x x x y y y这条路径的权值和 / / /乘积
  2. 查询 x x x y y y这条路径的最大值 / / /最小值
  3. 查询以 x x x为根的子树内的权值和 / / /乘积
  4. 查询以 x x x为根的子树内的最大值 / / /最小值

这些操作通过线段树维护

Q . Q. Q. 树链剖分的概念?
A . A. A.

  • 重儿子 指对于每个非叶子节点 它的儿子中 儿子数量最多的那个儿子
  • 轻儿子 指对于每个非叶子节点 其余的儿子 ( ( (非重儿子 ) ) )
  • 重边 连接任意两个重儿子的边
  • 轻边 非重边 其余的边即轻边
  • 重链 相邻重边连起来的 连接一条重儿子的链
  • 对于叶子节点 若其为轻儿子 则有一条以自己为起点的长度为 1 1 1的链
  • 每一条重链以轻儿子为起点

用两个 d f s dfs dfs 完成下列事情

d f s 1 : dfs1: dfs1:

  • 记录每个点的深度 d e p [ ] dep[] dep[]
  • 记录每个点的父亲 f a [ ] fa[] fa[]
  • 记录每个非叶子节点的子树大小 s i z e [ ] size[] size[]
  • 记录每个非叶子节点的重儿子编号 s o n [ ] son[] son[]

C O D E : CODE: CODE

void dfs(int x,int father,int deep)
{
	dep[x]=deep;  //深度
	fa[x]=father;  //父亲
	size[x]=1;  //子树大小
	int numson=-1;  //重儿子个数(可以不用)
	for(int i=head[x];i;i=edge[i].next)
	{
		int qwq=edge[i].to;
		if(qwq==father) continue;
		dfs(qwq,x,deep+1);
		size[x]+=size[qwq];
		if(size[qwq]>numson)
			son[x]=qwq,numson=size[qwq];
	}
}

d f s 2 : dfs2: dfs2:

  • 记录每个点的 d f s dfs dfs
  • 每个点的编号赋到dfs序编号上 ( ( (也可以直接赋初值 ) ) )
  • 处理每条链的顶端 以及链

C O D E : CODE: CODE

void dfs2(int x,int topN)
{
    dfn[x]=++num;  //dfs序
	id[num]=x;  //编号
	top[x]=topN;  //链的顶端
    if(!son[x]) return;
	dfs2(son[x],topN);
    for(int i=head[x];i;i=edge[i].next)
    {
        int qwq=edge[i].to;
        if(qwq==fa[x]||qwq==son[x]) continue;
        dfs2(qwq,qwq);  //见上面概念7
    }
}

区间查询 / / / 修改:

区间和查询例子

int queryS(int x,int y)
{
    int res=0;
    while(top[x]!=top[y])  //不在一条链上
    {
        if(dep[top[x]]<dep[top[y]])  //x改为深度更深
            swap(x,y);
        res+=SumQ(1,1,n,dfn[top[x]],dfn[x]);  //加上x点到x所在链顶端的点权和
        x=fa[top[x]];  //x跳到链顶端上面一个点
    }
    if(dep[x]>dep[y]) swap(x,y);  //同一条链了 深度改回去
    res+=SumQ(1,1,n,dfn[x],dfn[y]);  //再加上两个点的区间和
    return res;
}

修改也同样 把求和操作改为修改操作即可

线段树嘛 大家都会就不用贴了 q w q qwq qwq

例题 L u o g u P 3384   LuoguP3384~ LuoguP3384 轻重链剖分 / / /树链剖分

C O D E : CODE: CODE

小压了些行 仍有 180 180 180

#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,root,Mod,res;
int tot,head[N],w[N],f[N];
struct node{int to,next;}edge[N];
struct SegmentTree{int val,lazy;}a[N<<2];
int son[N],id[N],fa[N],dfn,dep[N],size[N],top[N];
void add(int x,int y){
	edge[++tot]=(node){y,head[x]};
	head[x]=tot;
}
void up(int x){a[x].val=(a[x<<1].val+a[x<<1|1].val)%Mod;}
void down(int x,int len)
{
	a[x<<1].lazy+=a[x].lazy;
	a[x<<1|1].lazy+=a[x].lazy;
	a[x<<1].val+=a[x].lazy*(len-(len>>1));
	a[x<<1|1].val+=a[x].lazy*(len>>1);
	a[x<<1].val%=Mod;
	a[x<<1|1].val%=Mod;
	a[x].lazy=0;
}
void build(int x,int l,int r)
{
	if(l==r)
	{
		a[x].val=w[f[l]];
		if(a[x].val>Mod) a[x].val%=Mod;
		return;
	}
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	up(x);
}
void query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)
	{
		(res+=a[x].val)%=Mod;
		return;
	}
	else{
		int mid=(l+r)>>1;
		if(a[x].lazy) down(x,r-l+1);
		if(L<=mid) query(x<<1,l,mid,L,R);
		if(mid<R) query(x<<1|1,mid+1,r,L,R);
	}
}
void update(int x,int l,int r,int L,int R,int k)
{
	if(L<=l&&r<=R)
	{
		a[x].lazy+=k;
		a[x].val+=k*(r-l+1);
	}
	else{
		int mid=(l+r)>>1;
		if(a[x].lazy) down(x,r-l+1);
		if(L<=mid) update(x<<1,l,mid,L,R,k);
		if(mid<R) update(x<<1|1,mid+1,r,L,R,k);
		up(x);
	}
}
int queryR(int x,int y)
{
	int ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res=0;
		query(1,1,n,id[top[x]],id[x]);
		(ans+=res)%=Mod;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	res=0;
	query(1,1,n,id[x],id[y]);
	ans+=res;
	return ans%Mod;
}
void updateR(int x,int y,int k)
{
	k%=Mod;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		update(1,1,n,id[top[x]],id[x],k);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	update(1,1,n,id[x],id[y],k);
}
int queryS(int x)
{
	res=0;
	query(1,1,n,id[x],id[x]+size[x]-1);
	return res;
}
void updateS(int x,int k){update(1,1,n,id[x],id[x]+size[x]-1,k);}
void dfs(int x,int father,int deep)
{
	dep[x]=deep;
	fa[x]=father;
	size[x]=1;
	int numson=-1;
	for(int i=head[x];i;i=edge[i].next)
	{
		int qwq=edge[i].to;
		if(qwq==father) continue;
		dfs(qwq,x,deep+1);
		size[x]+=size[qwq];
		if(size[qwq]>numson)
			son[x]=qwq,numson=size[qwq];
	}
}
void dfs2(int x,int topN)
{
	id[x]=++dfn;
	f[dfn]=x;
	top[x]=topN;
	if(!son[x]) return;
	dfs2(son[x],topN);
	for(int i=head[x];i;i=edge[i].next)
	{
		int qwq=edge[i].to;
		if(qwq==fa[x]||qwq==son[x]) continue;
		dfs2(qwq,qwq);
	}
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&root,&Mod);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	for(int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(root,0,1);
	dfs2(root,root);
	build(1,1,n);
	while(m--)
	{
		int op,x,y,k;
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%d",&x,&y,&k);
			updateR(x,y,k);
		}
		else if(op==2)
		{
			scanf("%d%d",&x,&y);
			printf("%d\n",queryR(x,y));
		}
		else if(op==3)
		{
			scanf("%d%d",&x,&y);
			updateS(x,y);
		}
		else
		{
			scanf("%d",&x);
			printf("%d\n",queryS(x));
		}
	}
	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值