树链剖分 模板小记

\[树链剖分~模板小记 \]


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

  1. \(x\)节点\(+~or\times val\)
  2. \(x\)\(y\)这条路径上所有节点\(+~or\times val\)
  3. 将以\(x\)为根的子树内所有节点\(+~or\times val\)

\(A_2.\) 查询的有:

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

这些操作通过线段树维护

\(Q.\) 树链剖分的概念?
\(A.\)

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

用两个\(dfs\) 完成下列事情

\(dfs1:\)

  • 记录每个点的深度\(dep[]\)
  • 记录每个点的父亲\(fa[]\)
  • 记录每个非叶子节点的子树大小\(size[]\)
  • 记录每个非叶子节点的重儿子编号\(son[]\)

\(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];
	}
}

\(dfs2:\)

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

\(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;
}

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

线段树嘛 大家都会就不用贴了\(qwq\)

例题 \(LuoguP3384~\)轻重链剖分\(/\)树链剖分

\(CODE:\)

小压了些行 仍有\(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;
}
posted @ 2022-01-19 21:51  EschatonRin  阅读(6)  评论(0)    收藏  举报