【模板】树链剖分

树链剖分在算法竞赛之中的运用和线段树一般灵活,所以说没有什么固定化的模板,只能以某些功能为例写模板。
这里就写出能够快速维护下面一些操作的树链剖分模板:
将树从x到y结点最短路径上所有节点的值都加上z
求树从x到y结点最短路径上所有节点的值之和

这里首先给出的不是普通的静态数组树链剖分,而是链表版本的树链剖分(连线段树也是链表版本的),假如已经做好准备,就继续看下去吧。

先说一声,这篇博文就是orz了大佬博客后写的,大体风格差不多
这里写图片描述
在这幅图中,用蓝线描好的是重边,重边连起来形成重链,其余的都是轻边。
先上了一堆树链剖分的专用术语,现在要对它们解释一下了。

重儿子:一个非叶子结点的各个儿子中拥有最大子树的儿子
重边:一个非叶子结点与它的重儿子相连的边
轻边:概念与重边相对,非叶子结点与轻儿子相连的边
重链:若干条重边首尾相连形成重链

这种剖分方法是有理由的。

首先,对于轻边(U,V),size(V)<=size(U)/2。很好证,瞪眼法即可。

第二,从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重链。

第二个性质很重要,这直接决定了树链剖分的 O ( n l o g n ) 的高效的时间复杂度。


结构体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;
}

猜你喜欢

转载自blog.csdn.net/diogenes_/article/details/80186602