树链剖分在算法竞赛之中的运用和线段树一般灵活,所以说没有什么固定化的模板,只能以某些功能为例写模板。
这里就写出能够快速维护下面一些操作的树链剖分模板:
将树从x到y结点最短路径上所有节点的值都加上z
求树从x到y结点最短路径上所有节点的值之和
这里首先给出的不是普通的静态数组树链剖分,而是链表版本的树链剖分(连线段树也是链表版本的),假如已经做好准备,就继续看下去吧。
先说一声,这篇博文就是orz了大佬博客后写的,大体风格差不多
在这幅图中,用蓝线描好的是重边,重边连起来形成重链,其余的都是轻边。
先上了一堆树链剖分的专用术语,现在要对它们解释一下了。
重儿子:一个非叶子结点的各个儿子中拥有最大子树的儿子
重边:一个非叶子结点与它的重儿子相连的边
轻边:概念与重边相对,非叶子结点与轻儿子相连的边
重链:若干条重边首尾相连形成重链
这种剖分方法是有理由的。
首先,对于轻边(U,V),size(V)<=size(U)/2。很好证,瞪眼法即可。
第二,从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重链。
第二个性质很重要,这直接决定了树链剖分的 的高效的时间复杂度。
结构体s
struct Node { //结点信息
struct Edge *head; //前向星存边
struct Chain *chain; //所存链的信息
Node *father, *son; //父亲节点和儿子结点
int size, dfn, depth, value; //size表示以该节点为根的子树中结点数
//dfn记录DFS序,线段树维护
//depth记录结点深度
//value记录权值
bool visit; //visit记录是否访问,DFS中运用
}info[MAXN];
struct Edge { //前向星存边信息
Node *from, *to;
Edge *next;
Edge(Node *from, Node *to) : from(from), to(to), next(from->head) {} //构造函数
};
struct Chain { //重链信息
Node *top; //链顶信息
Chain(Node *top) : top(top) {}
};
加边
inline void AddEdge(int from, int to) {
info[from].head = new Edge(&info[from], &info[to]);
info[to].head = new Edge(&info[to], &info[from]);
}
做法
树链剖分分为两步(后续还要线段树维护信息),由两遍DFS完成。
第一遍DFS 找重边
void dfs1(Node *u) { //当前结点u
u->visit = true; //标记已访问
u->size = 1; //首先更新u为根子树结点数为1(后面统计)
for (Edge *e = u->head; e; e = e->next) {
Node *v = e->to;
if (!v->visit) {
v->father = u;
v->depth = u->depth + 1;
dfs1(v);
u->size += v->size; //u的size更新,加上子节点的size
if (!u->son || u->son->size < v->size)
u->son = v; //判断v是否是u的重儿子
}
}
}
第二遍DFS 连重边成重链
void dfs2(Node *u) {
static int dfs_clock = 0; //时间戳
u->dfn = ++dfs_clock; //敲一个时间戳
if (!u->father || u != u->father->son) //如果重链断了重开一条
u->chain = new Chain(u);
else u->chain = u->father->chain; //否则就接上
if (u->son) dfs2(u->son); //重儿子DFS
for (Edge *e = u->head; e; e = e->next) {
Node *v = e->to;
if (v->father == u && v != u->son)
dfs2(v); //轻儿子DFS
}
}
split
inline void split(int root) {
info[root].depth = 1;
dfs1(&info[root]); //由于DFS调用指针,接口输入要取地址
dfs2(&info[root]);
}
线段树部分
可以参考这
具体不解释,value在之后逐个加入,所以构造函数只有一个
struct SegTree {
int l, r;
SegTree *lc, *rc;
long long sum, add;
SegTree(int l, int r, SegTree *lc, SegTree *rc)
: l(l), r(r), lc(lc), rc(rc), sum(0), add(0) {}
void cover(long long rhs) {
add += rhs;
sum += rhs * (r - l + 1);
}
void pushup() {
sum = lc->sum + rc->sum;
}
void pushdown() {
lc->cover(add);
rc->cover(add);
add = 0;
}
void update(int l, int r, long long rhs) {
if (l > this->r || r < this->l) return;
else if (l <= this->l && this->r <= r) cover(rhs);
else {
pushdown();
lc->update(l, r, rhs);
rc->update(l, r, rhs);
pushup();
}
return;
}
long long query(int l, int r) {
if (l > this->r || r < this->l) return 0;
if (l <= this->l && this->r <= r) return sum;
pushdown();
return lc->query(l, r) + rc->query(l, r);
}
static SegTree *build(int l, int r) {
if (l > r) return NULL;
else if (l == r) return new SegTree(l, r, NULL, NULL);
else {
int mid = (l + r) >> 1;
return new SegTree(l, r, build(l, mid), build(mid + 1, r));
}
}
}*root;
树链剖分更新部分
本来要对每条重链开一棵线段树,为了方便,我们只开一棵线段树。所以在输入之后无法直接在线段树上面进行修改。由于只能让x和y在同一条重链上才能进行方便的修改,所以要让两个结点跳到同一条重链上
void update(int x, int y, int z) { //x和y的路径上的所有点的点权增加z
Node *u = &info[x], *v = &info[y]; //找到指向x和y的指针
while (u->chain != v->chain) { //如果u和v不在同一条重链上
if (u->chain->top->depth < v->chain->top->depth) std::swap(u, v); //让u所在的重链深度更深
root->update(u->chain->top->dfn, u->dfn, z); //修改u所在重链上的信息
u = u->chain->top->father; //u跳到上一条重链上
}
if (u->depth > v->depth) std::swap(u, v); //让u深度更浅
root->update(u->dfn, v->dfn, z); //由于u和v已经在同一条重链上,直接修改
}
树链剖分查询部分
与updata类似,只有当两个结点在同一条重链上才可以直接查询,所以基本操作还是跳。
long long query(int x, int y) {
Node *u = &info[x], *v = &info[y];
long long result = 0;
while (u->chain != v->chain) { //跳的过程
if (u->chain->top->depth < v->chain->top->depth) std::swap(u, v);
result += root->query(u->chain->top->dfn, u->dfn); //查询累加
u = u->chain->top->father;
}
if (u->depth > v->depth) std::swap(u, v);
result += root->query(u->dfn, v->dfn); //查询
return result;
}
完整代码
struct Node {
struct Edge *head;
struct Chain *chain;
Node *father, *son;
int size, dfn, depth, value;
bool visit;
}info[MAXN];
struct Edge {
Node *from, *to;
Edge *next;
Edge(Node *from, Node *to) : from(from), to(to), next(from->head) {}
};
struct Chain {
Node *top;
Chain(Node *top) : top(top) {}
};
inline void AddEdge(int from, int to) {
info[from].head = new Edge(&info[from], &info[to]);
info[to].head = new Edge(&info[to], &info[from]);
}
void dfs1(Node *u) {
u->visit = true;
u->size = 1;
for (Edge *e = u->head; e; e = e->next) {
Node *v = e->to;
if (!v->visit) {
v->father = u;
v->depth = u->depth + 1;
dfs1(v);
u->size += v->size;
if (!u->son || u->son->size < v->size)
u->son = v;
}
}
}
void dfs2(Node *u) {
static int dfs_clock = 0;
u->dfn = ++dfs_clock;
if (!u->father || u != u->father->son)
u->chain = new Chain(u);
else u->chain = u->father->chain;
if (u->son) dfs2(u->son);
for (Edge *e = u->head; e; e = e->next) {
Node *v = e->to;
if (v->father == u && v != u->son)
dfs2(v);
}
}
inline void split(int root) {
info[root].depth = 1;
dfs1(&info[root]);
dfs2(&info[root]);
}
struct SegTree {
int l, r;
SegTree *lc, *rc;
long long sum, add;
SegTree(int l, int r, SegTree *lc, SegTree *rc)
: l(l), r(r), lc(lc), rc(rc), sum(0), add(0) {}
void cover(long long rhs) {
add += rhs;
sum += rhs * (r - l + 1);
}
void pushup() {
sum = lc->sum + rc->sum;
}
void pushdown() {
lc->cover(add);
rc->cover(add);
add = 0;
}
void update(int l, int r, long long rhs) {
if (l > this->r || r < this->l) return;
else if (l <= this->l && this->r <= r) cover(rhs);
else {
pushdown();
lc->update(l, r, rhs);
rc->update(l, r, rhs);
pushup();
}
return;
}
long long query(int l, int r) {
if (l > this->r || r < this->l) return 0;
if (l <= this->l && this->r <= r) return sum;
pushdown();
return lc->query(l, r) + rc->query(l, r);
}
static SegTree *build(int l, int r) {
if (l > r) return NULL;
else if (l == r) return new SegTree(l, r, NULL, NULL);
else {
int mid = (l + r) >> 1;
return new SegTree(l, r, build(l, mid), build(mid + 1, r));
}
}
}*root;
void update(int x, int y, int z) {
Node *u = &info[x], *v = &info[y];
while (u->chain != v->chain) {
if (u->chain->top->depth < v->chain->top->depth) std::swap(u, v);
root->update(u->chain->top->dfn, u->dfn, z);
u = u->chain->top->father;
}
if (u->depth > v->depth) std::swap(u, v);
root->update(u->dfn, v->dfn, z);
}
long long query(int x, int y) {
Node *u = &info[x], *v = &info[y];
long long result = 0;
while (u->chain != v->chain) {
if (u->chain->top->depth < v->chain->top->depth) std::swap(u, v);
result += root->query(u->chain->top->dfn, u->dfn);
u = u->chain->top->father;
}
if (u->depth > v->depth) std::swap(u, v);
result += root->query(u->dfn, v->dfn);
return result;
}