树 链 剖 分 模 板 小 记 树链剖分~模板小记 树链剖分 模板小记
Q
.
Q.
Q. 树链剖分的背景?
A
.
A.
A. 连通且无环的树
Q
.
Q.
Q. 树链剖分的作用?
A
.
A.
A. 大幅增加码量 (
A
.
A.
A. 将一棵树变成几条链 树形变成线形 减少处理难度
Q
.
Q.
Q. 树链剖分能处理哪些问题?
A
1
.
A_1.
A1. 修改的有:
- 将 x x x节点 + o r × v a l +~or\times val + or×val
- 将 x x x到 y y y这条路径上所有节点 + o r × v a l +~or\times val + or×val
- 将以 x x x为根的子树内所有节点 + o r × v a l +~or\times val + or×val
A 2 . A_2. A2. 查询的有:
- 查询 x x x到 y y y这条路径的权值和 / / /乘积
- 查询 x x x到 y y y这条路径的最大值 / / /最小值
- 查询以 x x x为根的子树内的权值和 / / /乘积
- 查询以 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;
}