什么是树链剖分?树链剖分有什么作用?
字面意思,就是把一颗树拆成几个链,这些链上的节点序号是连续的,那么对于树上的路径的一些问题就可以转化为区间问题,就可以用熟悉的一些数据结构来维护了
一些需要知道的定义
重/轻 儿子:对于父节点来说,他的儿子节点中size最大的就是重儿子其他都是轻儿子,如果所有儿子节点size都相同,那么任意选一个作为重儿子
重/轻边父节点与他重儿子连的边是重边,其余是轻边
重/轻链,重边连起来的就是重链
推出的性质
1.如果u与v是一条轻边,那么size[v]*2<size[u]
2.任意两点之间的路径最多包含logn段重链
代码中需要的数组:
int w[N],fa[N],son[N],dep[N],top[N];
int sz[N],nw[N],id[N];
vector<int> ed[N];
其中w是最初的权值,fa是父节点,son是重儿子,dep是深度,top是链的顶点,sz是大小,id是新的序号,nw是新的序号对应的权值
我们可以用两次dfs来求出这些信息
void dfs1(int u,int father,int depth) //求重儿子
{
dep[u]=depth,fa[u]=father,sz[u]=1;
for(auto x:ed[u])
{
if(x==father) continue;
dfs1(x,u,depth+1);
sz[u]+=sz[x];
if(sz[son[u]]<sz[x]) son[u]=x;//如果节点sze大于重儿子,那么重儿子就是新的节点
}
}
void dfs2(int u,int t) //t代表的是当前重链的顶点
{
id[u]=++cnt,nw[cnt]=w[u],top[u]=t;
if(!son[u]) return ;
dfs2(son[u],t);//对于重儿子来说,top仍然是现在的top
for(auto x:ed[u])
{
if(x==fa[u]||x==son[u]) continue;
dfs2(x,x);//对于轻儿子,轻链所在的顶点就是自己
}
}
线段树的经典操作
struct stree{
int l,r;
ll sum,add;
}st[N<<2];
void pushup(int u)
{
st[u].sum=st[u<<1].sum+st[u<<1|1].sum;
}
void pushdown(int u)
{
if(st[u].add)
{
st[u<<1].sum+=1ll*st[u].add*(st[u<<1].r-st[u<<1].l+1);
st[u<<1|1].sum+=1ll*st[u].add*(st[u<<1|1].r-st[u<<1|1].l+1);
st[u<<1].add+=st[u].add,st[u<<1|1].add+=st[u].add;
st[u].add=0;
}
}
void build(int u,int l,int r)
{
st[u]={l,r,0,0};
if(l==r) st[u].sum=nw[l];
else
{
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int k)
{
if(st[u].l>=l&&st[u].r<=r) st[u].sum+=1ll*k*(st[u].r-st[u].l+1),st[u].add+=k;
else
{
pushdown(u);
int mid=st[u].l+st[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
}
ll query(int u,int l,int r)
{
if(st[u].l>=l&&st[u].r<=r) return st[u].sum;
ll res=0;
pushdown(u);
int mid=st[u].l+st[u].r>>1;
if(l<=mid) res+=query(u<<1,l,r);
if(r>mid) res+=query(u<<1|1,l,r);
return res;
}
剩下的就是一个问题了:怎样求出两点之间的所有重链呢?
比较像倍增求lca问题,当两个节点没有处于同一条链的时候,就先取出链的top的dep较大的那一条链,也就是先把下边的链取出,然后开始向根节点爬,直到他俩在一条链里
对于修改一个子树的情况,就要简单很多,父节点的dfs序一定是最小的,对于这一颗子树,他的序号都是连续的,所以只需要加上sze就可以
void update_path(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
modify(1,id[top[u]],id[u],k);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
modify(1,id[v],id[u],k);
}
void update_tree(int u,int k)
{
modify(1,id[u],id[u]+sz[u]-1,k);
}
ll query_path(int u,int v)
{
ll res=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res+=query(1,id[top[u]],id[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
res+=query(1,id[v],id[u]);
return res;
}
ll query_tree(int u)
{
return query(1,id[u],id[u]+sz[u]-1);
}