树链剖分
分为重链剖分和长链剖分,
这篇博客现只谈论重链剖分 我不会告诉你我还不会长链剖分 QAQ
大概操作就是通过两层Dfs将一棵树剖为数个链,再用线段树处理,可将树形结构变为线型结构,便于操作。 也就是优雅地暴力
一般可用于处理一些树上问题,比如LCA
上代码
#define combo void
#define eakag bool
#define eafoo int
#define fa(x) tree[x].fa
#define id(x) tree[x].id
#define dep(x) tree[x].dep
#define siz(x) tree[x].siz
#define son(x) tree[x].son
#define top(x) tree[x].top
struct SLPF{
struct Edge
{
int dep, siz, son, top, fa, id;
}tree[maxn];
int cnt;
/*
dfs1
1.标记每个点的深度
2.标记每个点的父亲
3.标记每个非叶子节点的子树的大小
4.标记每个非叶子节点的重儿子编号
*/
combo Dfs1(int u, int faa, int deep)
{
dep(u) = deep, fa(u) = faa, siz(u) = 1;
int maxson = -1;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == faa) continue;
Dfs1(v, u, deep + 1);
siz(u) += siz(v);
if(siz(v) > maxson)
son(u) = v, maxson = siz(v);
}
return;
}
/*
1.标记每个点的新编号
2.赋值每个点的初始值到新编号上
3.处理每个点所在链的顶端
4.处理每条链
*/
combo Dfs2(int u, int topf)
{//topf指当前链的最顶端节点
id(u) = ++cnt,//标记每个节点的新编号
top(u) = topf,//这个点所在链的顶端
at[cnt] = a[u];//把每个点的初始值赋给新编号
if(! son(u))return;
Dfs2(son(u), topf);
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == fa(u) || v == son(u)) continue;
Dfs2(v, v);
}
return;
}
eafoo get_lca(int x, int y)
{
while(top(x) != top(y))
{
if(dep(top(x)) < dep(top(y)))
swap(x, y);
x = fa(top(x));
}
return dep(x) > dep(y) ? y : x;
}
combo Updata_Range(int x, int y, int k)
{//将节点x到y节点的最短路径上的点的权值均加k
while(top(x) != top(y))
{
if(dep(top(x)) < dep(top(y)))
swap(x, y);
T.Updata(1, 1, n, id(top(x)), id(x), k);
x = fa(top(x));
}
if(dep(x) > dep(y)) swap(x,y);
T.Updata(1, 1, n, id(x), id(y), k);
return;
}
combo Updata_val(int x, int k)
{
return (combo)(T.updata(1, 1, n, id(x), k));
}
combo Updata_Son(int x, int k)
{//将以节点x为根节点的子树上的点的权值均加k
return (combo)(T.Updata(1, 1, n, id(x), id(x) + siz(x) - 1, k));
}
eafoo Query_Range(int x, int y)
{//求节点x到节点y上所有点的权值之和
int ans = 0;
while(top(x) != top(y))
{
if(dep(top(x)) < dep(top(y)))
swap(x,y);
ans += T.Query(1, 1, n, id(top(x)), id(x));
x = fa(top(x));
}
if(dep(x) > dep(y)) swap(x,y);
ans += T.Query(1, 1, n, id(x), id(y));
return ans;
}
eafoo Query_Son(int x)
{//求以x为根节点的子树的权值之和
return T.Query(1, 1, n, id(x), id(x) + siz(x) - 1);
}
}S;
剩下的就是个线段树的板子
#define combo void
#define eakag bool
#define eafoo int
#define val(x) S_tree[x].val
#define lazy(x) S_tree[x].lazy
struct Segment_tree
{
struct segment_tree
{
int val, lazy;
}S_tree[maxn];
inline combo pushup(int rt)
{
return (combo)(val(rt) = (val(rt << 1) + val(rt << 1|1)));
}
inline combo pushdown(int rt, int l, int r)
{
if(lazy(rt))
{
int mid = (l + r) >> 1;
lazy(rt << 1) += lazy(rt),
lazy(rt << 1|1) += lazy(rt);
val(rt << 1) += lazy(rt) * (mid - l + 1),
val(rt << 1|1) += lazy(rt)*(r - mid);
lazy(rt) = 0;
}
return;
}
combo Build(int rt, int l, int r)
{
if(l == r)
{
val(rt) = at[l];
}
else{
int mid = (l + r) >> 1;
Build(rt<<1, l, mid);
Build(rt<<1|1, mid + 1, r);
pushup(rt);
}
return;
}
combo updata(int rt, int l, int r, int k, int val)
{
if(l == r)
{
val(rt) += val;
return;
}
else
{
pushdown(rt, l, r);
int mid = (l + r) >> 1;
if(k <= mid)
updata(rt << 1, l, mid, k, val);
else updata(rt << 1 | 1, mid + 1, r, k, val);
pushup(rt);
return;
}
}
combo Updata(int rt, int l, int r, int L, int R, int k){
if(L <= l && r <= R)
{
lazy(rt) += k, val(rt) += k * (r - l + 1);
}
else
{
pushdown(rt, l, r);
int mid = (l + r) >> 1;
if(L <= mid) Updata(rt << 1, l, mid, L, R, k);
if(R > mid) Updata(rt << 1|1, mid + 1, r, L, R, k);
pushup(rt);
}
return;
}
eafoo Query(int rt, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return val(rt);
}
else
{
pushdown(rt, l, r);
int mid = (l + r) >> 1, ans = 0;
if(L <= mid) ans += Query(rt << 1, l, mid, L, R);
if(R > mid) ans += Query(rt << 1|1, mid + 1, r, L, R);
return ans;
}
}
}T;