重链剖分与线段树

树链剖分将整棵树可以铺到线性的去维护,于是利用线段树等可维护线性数组的数据结构,就可以做到很多事情了

当然也包括省赛的 J 题 -- 奇偶最小生成树,并且线段树还支持修改操作,这是 ST 表与普通倍增维护做不到的


这是没有模数的代码:

int n, m;
ll w[N];

int head[N], cnt;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int deep[N], fa[N], siz[N], son[N];
int top[N], dfn[N], tim;
ll nw[N];

void dfs1(int u, int father){
    fa[u] = father;
    deep[u] = deep[father] + 1;
    siz[u] = 1;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == father) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
    }
}
void dfs2(int u, int topx){
    dfn[u] = ++tim;
    nw[tim] = w[u];
    top[u] = topx;
    if(!son[u]) return ; // 说明叶子节点了
    dfs2(son[u], topx);
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

int ls(int p){return p << 1;}
int rs(int p){return p << 1|1;}

class SegmentTree{
public:
    ll tree[N << 2|1], tag[N << 2 |1];
    inline void push_up(int p){
        tree[p] = tree[ls(p)] + tree[rs(p)];
    }
    inline void add(int p, int pl, int pr, ll d){
        tree[p] += (pr - pl + 1)*d;
        tag[p] += d;
    }
    inline void build(int p, int pl, int pr){
        tag[p] = 0;
        if(pl == pr){
            tree[p] = nw[pl];
            return ;
        }
        int mid = (pl + pr) >> 1;
        build(ls(p), pl, mid);
        build(rs(p), mid + 1, pr);
        push_up(p);
    }
    inline void push_down(int p, int pl, int pr){
        if(tag[p]){
            int mid = (pl + pr) >> 1;
            add(ls(p), pl, mid, tag[p]);
            add(rs(p), mid + 1, pr, tag[p]);
            tag[p] = 0;
        }
    }
    inline void update(int L, int R, int p, int pl, int pr, ll d){
        if(L <= pl && pr <= R){
            add(p, pl, pr, d);
            return ;
        }
        push_down(p, pl, pr);
        int mid = (pl + pr) >> 1;
        if(L <= mid){
            update(L, R, ls(p), pl, mid, d);
        }
        if(R >= mid + 1){
            update(L, R, rs(p), mid + 1, pr, d);
        }
        push_up(p);
    }
    inline ll query(int L, int R, int p, int pl, int pr){
        if(L <= pl && pr <= R){
            return tree[p];
        }
        int mid = (pl + pr) >> 1;
        push_down(p, pl, pr);
        ll res = 0;
        if(L <= mid){
            res += query(L, R, ls(p), pl, mid);
        }
        if(R >= mid + 1){
            res += query(L, R, rs(p), mid + 1, pr);
        }
        return res;
    }
};
SegmentTree seg;

ll query_path(int x, int y){
    ll res = 0;
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]]) swap(x, y);
        res += seg.query(dfn[top[x]], dfn[x], 1, 1, n);
        x = fa[top[x]];
    }
    if(deep[x] < deep[y]) swap(x, y);
    res += seg.query(dfn[y], dfn[x], 1, 1, n);
    return res;
}

void update_path(int x, int y, ll dx){
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]]) swap(x, y);
        seg.update(dfn[top[x]], dfn[x], 1, 1, n, dx);
        x = fa[top[x]];
    }
    if(deep[x] < deep[y]) swap(x, y);
    seg.update(dfn[y], dfn[x], 1, 1, n, dx);
}

// 恐怕需要另外的数组,记录这个节点的开始 dfn 到结束 dfn

ll query_tree(int u){
    int l = dfn[u], r = dfn[u] + siz[u] - 1;
    return seg.query(l, r, 1, 1, n);
}

void update_tree(int u, ll x){
    int l = dfn[u], r = dfn[u] + siz[u] - 1;
    seg.update(l, r, 1, 1, n, x);
}

以及 main 函数

void solve() {
    int root;
    cin >> n >> m >> root >> mod;
    for(int i = 1; i <= n; i++){
        cin >> w[i];
    }
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    dfs1(root, 0);
    dfs2(root, root);

    seg.build(1, 1, n);
    for(int i = 1; i <= m; i++){
        int op, x, y, z;
        cin >> op;
        if(op == 1){
            cin >> x >> y >> z;
            update_path(x, y, z);
        }else if(op == 2){
            cin >> x >> y;
            cout << query_path(x, y) << endl;
            // ask;
        }else if(op == 3){
            cin >> x >> z;
            update_tree(x, z);
        }else{
            // ask;
            cin >> x;
            cout << query_tree(x) << endl;
        }
    }
}

这是有模数的版本:

点击查看代码
// Created by qyy on 2024/6/14.

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define PII pair<int, int>
#define endl "\n"

const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int mod;

int n, m;
ll w[N];

int head[N], cnt;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int deep[N], fa[N], siz[N], son[N];
int top[N], dfn[N], tim;
ll nw[N];

void dfs1(int u, int father){
    fa[u] = father;
    deep[u] = deep[father] + 1;
    siz[u] = 1;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == father) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(!son[u] || siz[son[u]] < siz[v]) son[u] = v; // 此行是修改之后的,离谱,很显然是这样啊......
    }
}
void dfs2(int u, int topx){
    dfn[u] = ++tim;
    nw[tim] = w[u];
    top[u] = topx;
    if(!son[u]) return ; // 说明叶子节点了
    dfs2(son[u], topx);
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

int ls(int p){return p << 1;}
int rs(int p){return p << 1|1;}

class SegmentTree{
public:
    ll tree[N << 2|1], tag[N << 2 |1];
    inline void push_up(int p){
        tree[p] = (tree[ls(p)] + tree[rs(p)]) % mod;
    }
    inline void add(int p, int pl, int pr, ll d){
        tree[p] = (tree[p] + (((ll)pr - (ll)pl + 1LL)*d) % mod) % mod;
        tag[p] += d;
        tag[p] %= mod;
    }
    inline void build(int p, int pl, int pr){
        tag[p] = 0;
        if(pl == pr){
            tree[p] = nw[pl];
            return ;
        }
        int mid = (pl + pr) >> 1;
        build(ls(p), pl, mid);
        build(rs(p), mid + 1, pr);
        push_up(p);
    }
    inline void push_down(int p, int pl, int pr){
        if(tag[p]){
            int mid = (pl + pr) >> 1;
            add(ls(p), pl, mid, tag[p]);
            add(rs(p), mid + 1, pr, tag[p]);
            tag[p] = 0;
        }
    }
    inline void update(int L, int R, int p, int pl, int pr, ll d){
        if(L <= pl && pr <= R){
            add(p, pl, pr, d);
            return ;
        }
        push_down(p, pl, pr);
        int mid = (pl + pr) >> 1;
        if(L <= mid){
            update(L, R, ls(p), pl, mid, d);
        }
        if(R >= mid + 1){
            update(L, R, rs(p), mid + 1, pr, d);
        }
        push_up(p);
    }
    inline ll query(int L, int R, int p, int pl, int pr){
        if(L <= pl && pr <= R){
            return tree[p];
        }
        int mid = (pl + pr) >> 1;
        push_down(p, pl, pr);
        ll res = 0;
        if(L <= mid){
            res += query(L, R, ls(p), pl, mid);
            res %= mod;
        }
        if(R >= mid + 1){
            res += query(L, R, rs(p), mid + 1, pr);
            res %= mod;
        }
        return res;
    }
};
SegmentTree seg;

ll query_path(int x, int y){
    ll res = 0;
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]]) swap(x, y);
        res += seg.query(dfn[top[x]], dfn[x], 1, 1, n);
        res %= mod;
        x = fa[top[x]];
    }
    if(deep[x] < deep[y]) swap(x, y);
    res += seg.query(dfn[y], dfn[x], 1, 1, n);
    return res % mod;
}

void update_path(int x, int y, ll dx){
    while(top[x] != top[y]){
        if(deep[top[x]] < deep[top[y]]) swap(x, y);
        seg.update(dfn[top[x]], dfn[x], 1, 1, n, dx);
        x = fa[top[x]];
    }
    if(deep[x] < deep[y]) swap(x, y);
    seg.update(dfn[y], dfn[x], 1, 1, n, dx);
}

// 恐怕需要另外的数组,记录这个节点的开始 dfn 到结束 dfn
ll query_tree(int u){
    int l = dfn[u], r = dfn[u] + siz[u] - 1;
    return seg.query(l, r, 1, 1, n);
}

void update_tree(int u, ll x){
    int l = dfn[u], r = dfn[u] + siz[u] - 1;
    seg.update(l, r, 1, 1, n, x);
}

void solve() {
    int root;
    cin >> n >> m >> root >> mod;
    for(int i = 1; i <= n; i++){
        cin >> w[i];
        w[i] %= mod;
    }
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    dfs1(root, 0);
    dfs2(root, root);

    seg.build(1, 1, n);
    for(int i = 1; i <= m; i++){
        int op, x, y, z;
        cin >> op;
        if(op == 1){
            cin >> x >> y >> z;
            update_path(x, y, z);
        }else if(op == 2){
            cin >> x >> y;
            cout << query_path(x, y) << endl;
            // ask;
        }else if(op == 3){
            cin >> x >> z;
            update_tree(x, z);
        }else{
            // ask;
            cin >> x;
            cout << query_tree(x) << endl;
        }
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t = 1;
    //cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}


2024.6.25 update: 写树上启发式合并 debug 时候发现 son[] 数组有问题,发现在这一行 ,于是更正

if(!son[u] || siz[son[u]] < siz[son[v]]) son[u] = v;
posted @   9102700  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
  1. 1 鼓楼 赵雷
  2. 2 我们的歌 王力宏
  3. 3 老街 李荣浩
  4. 4 周杰伦
  5. 5 可惜没如果 林俊杰
  6. 6 不将就 李荣浩
  7. 7 南方姑娘 赵雷
  8. 8 南方姑娘(弹唱版) 赵雷
  9. 9 如果可以 韦礼安
  10. 10 写给黄淮 邵帅
  11. 11 我想念 汪苏泷
  12. 12 雨天 汪苏泷
  13. 13 雨天雨天 汪苏泷
  14. 14 成都 赵雷
我们的歌 - 王力宏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王力宏/陈信延

作曲 : 王力宏

编曲 : 王力宏

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

情人总分分合合

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

电吉他/其他乐器:王力宏

鼓手:Eric Fawcrtt

贝斯:John Mumson

录音师/录音室:王力宏/Homeboy Music Studios,Taipei

OP:HIM Music Publishing Inc.

OP:Homeboy Music,Inc,Taiwan

SP:Sony Music Publishing(Pre)Ltd.Taiwan Branch

点击右上角即可分享
微信分享提示