首页 > 其他分享 >重链剖分与线段树

重链剖分与线段树

时间:2024-06-14 19:21:10浏览次数:19  
标签:pr ll 剖分 int 线段 mid dfn 重链 top

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

当然也包括省赛的 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[son[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[son[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;
}

标签:pr,ll,剖分,int,线段,mid,dfn,重链,top
From: https://www.cnblogs.com/9102qyy/p/18248494

相关文章

  • C138 线段树分治 P2056 [ZJOI2007] 捉迷藏
    视频链接:C138线段树分治P2056[ZJOI2007]捉迷藏_哔哩哔哩_bilibili   P2056[ZJOI2007]捉迷藏-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治O(nlognlogn)#include<iostream>#include<cstring>#include<algorithm>#include<vector>#inclu......
  • 线段树学习笔记
    线段树(SegmentTree)是一种基于分治思想的数据结构,可以在\(\mathcal{O}(\log~n)\)的时间内完成区间修改和区间查询等操作。1.1线段树基础此部分介绍普通线段树的基本思想与操作。1.1.1基本思想线段树本质上就是一棵二叉树,它的每一个节点表示一段区间的信息,对于一个长度不为......
  • 【模版】线段树
    https://www.luogu.com.cn/problem/P3372#include<cstring>#include<iostream>#include<algorithm>usingnamespacestd;#defineN100005#defineLLlonglong#definelcu<<1#definercu<<1|1LLw[N];structTree{//线段树LL......
  • 树链剖分
    基本概念将树中的点重新编号,使得树中任意一条路径,都可以转化成O(logn)段连续区间1.将一棵树转化成一个序列2.将树中的任意一段路径转化成logn段连续区间(线段树,树状数组)重链剖分重儿子:子树数量最多的根节点(只有一个,多个都是,任选一个即可)轻儿子:其余儿子重边:重儿......
  • C137 线段树分治 P2147 [SDOI2008] 洞穴勘测
    视频链接: P2147[SDOI2008]洞穴勘测-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治O(mlogmlogm)#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<map>usingnamespacestd;#definels(u<<1)......
  • 个字规则:轻松解决一大类初中数学三线段数量关系问题
    个字规则:轻松解决一大类初中数学三线段数量关系问题【题1】【题2】【题3】要点分析与方法提炼【题4】【题5】2024东城区二模【题6】2024朝阳区二模【题7】2024石景山区二模 ......
  • C136 线段树分治 P4219 [BJOI2014] 大融合
    视频链接: P4219[BJOI2014]大融合-洛谷|计算机科学教育新生态(luogu.com.cn)#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<map>usingnamespacestd;#definels(u<<1)#definers(u<<1|......
  • C135 线段树分治 P5631 最小mex生成树
    视频链接: P5631最小mex生成树-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治O(nlognlogw)#include<iostream>#include<cstring>#include<algorithm>#include<vector>usingnamespacestd;#definels(u<<1)#definers(u<<1|1)#d......
  • 编写一个程序,提示用户输入三个点 p0、p1 和 p2,显示 p2 是否在从 p0 到 p1 的线段左侧
    (几何:点的位置)给定一个从点p0(x0,y0)到pl(xl,pl)的有向线段,可以使用下面的条件来确定点p2(x2,y2)是在线段的左侧、右侧,或者在该直线上(见下图): 编写一个程序,提示用户输入三个点p0、p1和p2,显示p2是否在从p0到p1的线段左侧、右侧,或者在该直线上。下面是运行示例:......
  • vue3 + arcgis.js4.x---线段SimpleLineSymbol
    //polylineconstpolylineGraphic=newGraphic()polylineGraphic.geometry={type:'polyline',paths:[[117.227239,31.820586],[116.227239,33.820586]]}polylineGraphic.symbol=newSimpleLineSymbol({color:'#ff0000&#......