思想
对于每个节点,把所有子节点中子树最大的一个,成为重点,其它成为轻点。重点到父亲节点的连线成为重边,重边连接成若干条重链,其余的每个点称为重链。
可以发现,如果路径经过一条轻边,那么现在的子树大小至少缩小一半,所以每条路径可以被拆分成最多 \(\log n\) 条链,这样一来,就可以把较难维护的树形结构,改变成若干条链,并且可以发现按照先重后轻的原则遍历时,属于相同重链或相同子树的遍历序是连续的,就可以根据这个性质用线段树维护。
每个链在线段树上的区间是 \([dfn(top(u)),dfn(u)]\),每个子树在线段树上的区间是 \([dfn(u),dfn(u)+siz(u)-1]\),根据这个进行线段树操作。

模板
P3384 轻重链剖分/树链剖分
Code
#include
#include
#include
#include
#include
#include
#include
#include
#include
例题
1.P2690 ZJOI2008 书的统计
甚至比模板还要简单,没有复杂的转换,只需要维护区间和以及区间最大值,直接树剖套线段树。
代码鸽了。
2.P2486 SDOI2011 染色
一道很有趣的题。
因为要维护连续颜色段数,线段树的查询和修改就略有不同,函数参数应为:当前下标,当前区间,当前查询区间,要求查询区间,这样能够维护出边界的颜色,来特判左右两段是否合并为一个。
ACcode
#include
#include
#include
#include
#include
#include
#include
#include
#include
3.P2590 ZJOI2008 树的统计
模板题,代码不贴了。
4.P1967 货车运输
\(\operatorname{Kruskal}\)最大生成树重构一下,可以放到树剖里跑,因为维护的是树上边权最小值,可以把边权挂在子节点上,处理在同一重链的 \([L,R]\) 的路径时,线段树查询 \([L+1,R]\) 的最小值即可。
两个\(\operatorname{dfs}\)
ll fa[maxn],son[maxn],dep[maxn],siz[maxn];
ll dfn[maxn],top[maxn],tow[maxn],dfnw[maxn],dfncnt;
inline void dfs1(ll u,ll f,ll d){
dep[u]=d,fa[u]=f,siz[u]=1;
ll maxson=-1;
for(int i=head[u];i;i=e[i].nxt){
ll v=e[i].to;
if(v==f) continue;
tow[v]=e[i].w;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(siz[v]>maxson){
son[u]=v;
maxson=siz[v];
}
}
}
inline void dfs2(ll u,ll t){
dfn[u]=++dfncnt,dfnw[dfncnt]=tow[u],top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt){
ll v=e[i].to;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
查询操作
inline ll query_ptmin(ll u,ll v){
ll ans=maxxn;
while(top[u]!=top[v]){
if(dep[top[u]]
5.P3979 遥远的国度
换根的树剖,支持区间修改和区间维护最小值。
对于换根,因为树的性质不变,所以如果在初始化以 \(1\) 为根的树中,现在根节点和查询节点同属一颗子树的同左或同右的子树,就直接查询这个子树,如果在异侧那就找到子树的根,然后查询两次。
ACcode
#include
#include
#include
#include
#include
#include
#include
#include
#include
5.P1505 国家集训队 旅游
多用懒标记维护几个东西就ok了,因为要维护最大值以及最小值,所以取负的时候直接交换最大最小值。
放个上传和下传代码
inline void push_up(ll rt){
maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);
minx[rt]=min(minx[rt<<1],minx[rt<<1|1]);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
inline void push_down(ll rt){
laz[rt<<1]^=1,laz[rt<<1|1]^=1;
maxx[rt<<1]=-maxx[rt<<1],maxx[rt<<1|1]=-maxx[rt<<1|1];
minx[rt<<1]=-minx[rt<<1],minx[rt<<1|1]=-minx[rt<<1|1];
sum[rt<<1]=-sum[rt<<1],sum[rt<<1|1]=-sum[rt<<1|1];
swap(maxx[rt<<1],minx[rt<<1]);
swap(maxx[rt<<1|1],minx[rt<<1|1]);
laz[rt]=0;
}
6.P3258 JLOI2014 松鼠的新家
区间修改+单点查询
注意每次走的终点要减\(1\),因为会重复。
7.P2146 NOI2015 软件包管理器
- 安装操作:把根节点到当前节点路径赋值为\(1\)
- 卸载操作:当前节点子树赋值为\(0\)
输出变化量。