重链剖分
树链剖分
一、前置知识
- DFS序
- LCA最近公共祖先
- 线段树
二、定义
1.重儿子 \(son_u\)
节点 \(u\) 的儿子中, 子树最大的一个儿子称为重儿子。
2.重边
从父亲节点连向它的重儿子的边,称作重边。
3.重链
几条重边所组成的链叫重链,特别地,单个的点也可以称作一条重链。
4.\(top_u\) 重链顶
一条重链中深度最小的节点
三、树剖基础
顾名思义,他的做法就是分离出来每一条重链
由于求重链和重链顶不可以同时进行,因此这里有两个 DFS,来预处理。
DFS1:
void dfs1(int u){
sz[u] = 1;
for(int i = hd[u]; i; i = ed[i].nxt){
int v = ed[i].to;
if(v == fa[u]) continue;
fa[v] = u;//这里需要直接预处理fa[v],后面要用
dep[v] = dep[u] + 1;//预处理深度
dfs1(v);
sz[u] += sz[v];//预处理子树大小(求重儿子)
if(sz[v] > sz[son[u]]) son[u] = v;//重儿子
}
}
DFS2
void dfs2(int u, int t){//t为当前重链顶
top[u] = t, ver[++ ind] = a[u], dfn[u] = ind;//处理重链顶和DFS序(求LCA和线段树)
if(son[u]) dfs2(son[u], t);//只有重儿子才配当前的重链顶
for(int i = hd[u]; i; i = ed[i].nxt){
int v = ed[i].to;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
四、树剖LCA
思考:既然我们把一棵树都分成了一堆链,那么 LCA 就很好办了
两个节点一直往上跳重链顶,若两个点处于同一个链上,那么深度较深的哪个点就是 LCA
int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
五、树剖线段树
这种算法可以维护一个树上的信息(相较于线段树维护序列信息)。
具体例题可以看 (模板)树链剖分/重链剖分
如何把一棵树映射成一个序列呢?
可以记录下来这棵树的 DFS 序, 然后在这个序列上面进行操作,对于子树的问题,节点 \(u\) 的子树在 DFS 序中对应区间 \([dfn[u], dfn[u] + sz[u] - 1]\) 那么后两个问题迎刃而解。
那么,更进一步地,可以在 DFS 序中优先考虑重儿子。那么对于一条链上的操作就可以像上面的 LCA 一样,分成若干重链,在每个重链上面分别进行操作就可以了。
void add(int u, int v, int x){//链上加
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
mdf(dfn[top[u]], dfn[u], x);//线段树修改
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
mdf(dfn[u], dfn[v], x);
}
int sum(int u, int v){//链上求和
int res = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
res += qry(dfn[top[u]], dfn[u]);//线段树求和
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
return res + qry(dfn[u], dfn[v]);
}
总结
反正都是板子,改的地方很少...
Code
int fa[N], son[N], sz[N], top[N], dep[N], ver[N], dfn[N], ind;
namespace segtree{
#define ls (u << 1)
#define rs (u << 1 | 1)
#define mid (l + r >> 1)
#define segroot int u = 1, int l = 1, int r = n
#define lson ls, l, mid
#define rson rs, mid + 1, r
struct { int sum, add, len; } t[N << 2];
void up(int u) { t[u].sum = t[ls].sum + t[rs].sum; }
void build(segroot){
t[u].len = r - l + 1;
if(l == r){ t[u].sum = ver[l]; return; }
build(lson); build(rson); up(u);
}
void down(int u, int x){ t[u].add += x, t[u].sum += t[u].len * x; }
void down(int u) { down(ls, t[u].add), down(rs, t[u].add), t[u].add = 0; };
int qry(int ql, int qr, segroot) {
if( qr < l || r < ql ) return 0;
if( ql <= l && r <= qr ) return t[u].sum;
down(u);
return qry(ql, qr, lson) + qry(ql, qr, rson);
}
void mdf(int ql, int qr, int x, segroot) {
if( qr < l || r < ql ) return;
if( ql <= l && r <= qr ) return down(u, x);
down(u), mdf(ql, qr, x, lson), mdf(ql, qr, x, rson), up(u);
}
}
using namespace segtree;
struct Node{
int to, nxt;
} ed[N << 1];
int hd[N << 1], cnt;
void addedge(int u, int v){
ed[++ cnt] = {v, hd[u]};
hd[u] = cnt;
}
void dfs1(int u){
sz[u] = 1;
for(int i = hd[u]; i; i = ed[i].nxt){
int v = ed[i].to;
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t){
top[u] = t, ver[++ ind] = a[u], dfn[u] = ind;
if(son[u]) dfs2(son[u], t);
for(int i = hd[u]; i; i = ed[i].nxt){
int v = ed[i].to;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
void add(int u, int v, int x){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
mdf(dfn[top[u]], dfn[u], x);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
mdf(dfn[u], dfn[v], x);
}
int sum(int u, int v){
int res = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
res += qry(dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
return res + qry(dfn[u], dfn[v]);
}

浙公网安备 33010602011771号