洛谷:P3384 -【模板】树链剖分

树链剖分详情:跳转大佬博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html

题目链接:https://www.luogu.org/problemnew/show/P3384

解题心得:
个人看来其实树链剖分就是把一棵标号没有实际意义的树重新标号,标号的规则按照重链优先,在有序之后用线段树之类的数据结构来维护。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5+100;

struct Node {
    int sum, lazy;
}node[maxn<<2];

int num[maxn], n, m, rt, mod;
int Size[maxn], deep[maxn], max_son[maxn], father[maxn], top[maxn], rk[maxn], id[maxn], cnt;
/*
 Size[i] 代表以i为根节点的子树的大小
 deep[i] 代表节点i的深度
 max_son[i] 代表以i的子节点为根节点的树最大的子节点节点标号
 father[i] 表示i的父亲是谁
 top[i] 代表i所在的重链的根节点
 id[i] 代表标号为i的节点剖分之后的数
 rk[i] 剖分之后数为i的节点的标号是多少
 */

vector <int> ve[maxn];

void init() {
    scanf("%d%d%d%d",&n, &m, &rt, &mod);

    for(int i=1;i<=n;i++) scanf("%d", &num[i]);
    for(int i=1;i<n;i++) {
        int a, b; scanf("%d%d", &a, &b);
        ve[a].push_back(b);
        ve[b].push_back(a);
    }
}

int dfs(int pre, int now, int Deep) {
    Size[now] = 1;
    deep[now] = Deep;
    father[now] = pre;

    int Max = 0;

    for(int i=0;i<ve[now].size();i++) {
        int v = ve[now][i];
        if(v != pre) {
            Size[now] += dfs(now, v, Deep+1);
            if(Size[v] > Max) {
                Max = Size[v];
                max_son[now] = v;
            }
        }
    }

    return Size[now];
}

void dfs2(int pre, int now, int Top) {
    top[now] = Top;
    id[now] = ++cnt;
    rk[cnt] = now;

    if(!max_son[now]) return ;

    dfs2(now, max_son[now], Top);

    for(int i=0;i<ve[now].size();i++) {
        int v = ve[now][i];
        if(v != pre && v != max_son[now]) {
            dfs2(now, v, v);
        }
    }
}

void pushup(int root) {
    int chl = root<<1;
    int chr = root<<1|1;

    node[root].sum = (node[chl].sum + node[chr].sum)%mod;
}

void push_down(int root, int l, int r) {
    if(node[root].lazy == 0) return ;

    int chl = root <<1 ;
    int chr = root<<1|1;
    int mid = l + r >> 1;

    node[chl].lazy = (node[chl].lazy + node[root].lazy) % mod;
    node[chl].sum =  (node[chl].sum + (mid-l + 1) * node[root].lazy) % mod;

    node[chr].lazy = (node[chr].lazy + node[root].lazy) % mod;
    node[chr].sum = (node[chr].sum + (r - mid) * node[root].lazy) % mod;

    node[root].lazy = 0;
}

void build_tree(int root, int l, int r) {
    if(l == r) {
        int Id = rk[l];
        node[root].sum = num[Id];
        return ;
    }

    int mid = l + r >> 1;
    build_tree(root<<1, l, mid);
    build_tree(root<<1|1, mid+1, r);

    pushup(root);
}

void build_tree() {
    dfs(-1, rt, 1);
    dfs2(-1, rt, rt);

    build_tree(1, 1, n);
}

void add(int root, int ql, int qr, int l, int r, int c) {
    if(ql == l && qr == r) {
        node[root].lazy += c;
        node[root].sum = (node[root].sum + (r - l + 1) * c) % mod;
        return ;
    }
    push_down(root, l, r);

    int mid = l + r >> 1;
    if(mid >= qr)
        add(root<<1, ql, qr, l, mid, c);
    else if(mid < ql) {
        add(root<<1|1, ql, qr, mid+1, r, c);
    } else {
        add(root<<1, ql, mid, l, mid, c);
        add(root<<1|1, mid+1, qr, mid+1, r, c);
    }
    pushup(root);
}

void update(int l, int r, int c) {
    while(top[l] != top[r]) {
        if(deep[top[l]] < deep[top[r]]) swap(l, r);
        add(1, id[top[l]], id[l], 1, n, c);

        l = top[l]; l = father[l];
    }
    if(id[l] > id[r]) swap(l, r);
    add(1, id[l], id[r], 1, n, c);
}

int query(int root, int ql, int qr, int l, int r) {
    if(ql == l && qr == r) {
        return node[root].sum;
    }

    push_down(root, l, r);
    int mid = l + r >> 1;

    if(mid >= qr) {
        return query(root<<1, ql, qr, l, mid) % mod;
    } else if(mid < ql) {
        return query(root<<1|1, ql, qr, mid+1, r) % mod;
    } else {
        return (query(root<<1, ql, mid, l, mid) + query(root<<1|1, mid+1, qr, mid+1, r)) % mod;
    }
}

int query(int l, int r) {
    int Sum = 0;
    while(top[l] != top[r]) {
        if(deep[top[l]] < deep[top[r]]) swap(l, r);

        Sum += query(1, id[top[l]], id[l], 1, n);
        Sum %= mod;

        l = top[l]; l = father[l];
    }

    if(id[l] > id[r]) swap(l, r);
    Sum += query(1, id[l], id[r], 1, n);
    return Sum % mod;
}

int main() {
//    freopen("1.in", "r", stdin);
    init();
    build_tree();

    while(m--) {
        int op; scanf("%d", &op);
        if(op == 1) {
            int a, b, c;
            scanf("%d%d%d",&a, &b, &c);
            update(a, b, c);
        } else if(op == 2) {
            int a, b; scanf("%d%d", &a, &b);
            printf("%d\n", query(a, b));
        } else if(op == 3) {
            int pos, c; scanf("%d%d",&pos, &c);
            add(1, id[pos], id[pos]+Size[pos]-1, 1, n, c);
        } else {
            int pos; scanf("%d", &pos);
            printf("%d\n", query(1, id[pos], id[pos]+Size[pos]-1, 1, n));
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值