zoukankan      html  css  js  c++  java
  • 树链剖分

    树链剖分 - Acwing2568

    树链剖分:一个强行增加代码量的树形结构预处理,将树拆分成若干条链,以便用线段树等其他数据结构在树上进行区间操作。套上树链剖分后,将使得原本的各项区间操作复杂度再乘上一个logn(最坏情况下)。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 1e5+10;
    
    vector<int> link[N];
    
    int n, m;
    int a, b, op, k;
    int dfs_id[N];      // 节点的dfs序id
    int head_id[N];     // 节点所在重链的起始节点的id
    int fa[N];          // 父节点id
    int subtree_start[N];       // 子树对应的dfs序左端点
    int subtree_end[N];         // 子树对应的dfs序右端点
    int dep[N];         // 节点深度
    int heavy_son[N];   // 重儿子
    int is_heavy[N];    // 该节点是否为重儿子
    
    ll tree[N<<2];
    ll lazy[N<<2];
    
    ll arr[N];
    ll new_arr[N];
    
    
    void push_up(int rt){
        tree[rt] = tree[rt<<1] + tree[rt<<1|1];
    }
    
    void push_down(int rt,int l,int r){
        if(lazy[rt]){
            int mid = (l+r)>>1;
            tree[rt<<1] += (mid-l+1) * lazy[rt];
            tree[rt<<1|1] += (r-mid) * lazy[rt];
            lazy[rt<<1] += lazy[rt];
            lazy[rt<<1|1] += lazy[rt];
            lazy[rt] = 0;
        }
    }
    
    void build(int rt,int l,int r){
        if(l == r){
            tree[rt] = new_arr[l];
        }else{
            int mid = (l+r)>>1;
            build(rt<<1, l, mid);
            build(rt<<1|1, mid+1, r);
            push_up(rt);
        }
    }
    
    void modify(int rt,int l,int r,int ml,int mr,int v){
        if(ml <= l && mr >= r){
            tree[rt] += v * (r-l+1);
            lazy[rt] += v;
        }else{
            push_down(rt, l, r);
            int mid = (l+r)>>1;
            if(ml <= mid){
                modify(rt<<1, l, mid, ml, mr, v);
            }
            if(mr > mid){
                modify(rt<<1|1, mid+1, r, ml, mr, v);
            }
            push_up(rt);
        }
    }
    
    ll query(int rt,int l,int r,int ql,int qr){
        if(ql <= l && qr >= r){
            return tree[rt];
        }else{
            push_down(rt, l, r);
            ll ans = 0;
            int mid = (l+r)>>1;
            if(ql <= mid){
                ans += query(rt<<1, l, mid, ql, qr);
            }
            if(qr > mid){
                ans += query(rt<<1|1, mid+1, r, ql, qr);
            }
            return ans;
        }
    }
    
    
    void add_edge(int a,int b){
        link[a].push_back(b);
        link[b].push_back(a);
    }
    
    int dfs1(int cur,int prev,int d){
        fa[cur] = prev;     // 记录父节点
        dep[cur] = d;       // 记录深度
        int tot_size = 1;   // 以当前节点为根的子树的大小
        int max_size = -1,max_root = 0, cur_size = 0;
        
        for(auto it = link[cur].begin(); it != link[cur].end(); ++it){ // 遍历其每一个儿子
            if(*it != prev){ // 排除父节点
                cur_size = dfs1(*it, cur, d+1);
                if(cur_size > max_size){ // 更新重儿子
                    max_size = cur_size;
                    max_root = *it;
                }
                tot_size += cur_size;
            }
        }
        heavy_son[cur] = max_root;
        return tot_size;
    }
    
    
    void dfs2(int cur,int h,int prev,int &id){
        head_id[cur] = h;   // 节点所在重链的起始节点的id
        dfs_id[cur] = id++; // 记录dfs序
        
        
        if(heavy_son[cur]){ // 如果当前节点存在重儿子
            dfs2(heavy_son[cur], h, cur, id);
        }
        
        for(auto it = link[cur].begin(); it != link[cur].end(); ++it){
            if(*it != prev && *it != heavy_son[cur]){ // 排除父节点 & 排除重儿子(重儿子如果存在则已经被优先遍历过)
                    dfs2(*it, *it, cur, id);
            }
        }
    
        subtree_start[cur] = dfs_id[cur];
        subtree_end[cur] = id-1;
    }
    
    void build_lct(){
        // dfs1(int cur,int prev,int d)
        dfs1(1, 0, 1);
        int id = 1;
        // dfs2(int cur,int h,int prev,int &id)
        dfs2(1, 1, 0, id);
    }
    
    void modify_lct(int a,int b,int v){ // 在a和b的路径上增加v
        int ha,hb;
        while(1){
            ha = head_id[a];
            hb = head_id[b];
            
            if(ha == hb){
                a = dfs_id[a];
                b = dfs_id[b];
                if(a > b) swap(a, b);
                modify(1, 1, n, a, b, v);
                break;
            }else{
                if(dep[ha] < dep[hb]){
                    swap(a, b);
                    swap(ha, hb);
                }
                modify(1, 1, n, dfs_id[ha], dfs_id[a], v); // 这是一条重链
                a = fa[ha];
            }
        }
    }
    
    
    ll query_lct(int a,int b){
        int ha, hb;
        ll ans = 0;
        while (1) {
            ha = head_id[a];
            hb = head_id[b];
            
            if(ha == hb){
                a = dfs_id[a];
                b = dfs_id[b];
                if(a > b) swap(a, b);
                ans += query(1, 1, n, a, b);
                break;
            }else{
                if(dep[ha] < dep[hb]){
                    swap(a, b);
                    swap(ha, hb);
                }
                ans += query(1, 1, n, dfs_id[ha], dfs_id[a]);
                a = fa[ha];
            }
        }
        return ans;
    }
    
    
    void modify_sub(int id,int v){
        modify(1, 1, n, subtree_start[id], subtree_end[id], v);
    }
    
    ll query_sub(int id){
        return query(1, 1, n, subtree_start[id], subtree_end[id]);
    }
    
    int main(){
        scanf("%d",&n);
        for(int i = 1; i <= n; ++i){
            scanf("%d",&arr[i]);
        }
        
        for(int i = 0; i < n-1; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a, b);
        }
        
        build_lct();
        
        for(int i = 1; i <= n; ++i){
            new_arr[dfs_id[i]] = arr[i];
        }
        
        build(1, 1, n);
        
        scanf("%d",&m);
        while (m--) {
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%d%d",&a,&b,&k);
                modify_lct(a, b, k);
            }else if(op == 2){
                scanf("%d%d",&a,&k);
                modify_sub(a, k);
            }else if(op == 3){
                scanf("%d%d",&a,&b);
                printf("%lld
    ",query_lct(a, b));
            }else{
                scanf("%d",&a);
                printf("%lld
    ",query_sub(a));
            }
        }
        
        return 0;
    }
    
    
    
    
    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    01-移动端 REM 适配(postcss-pxtorem,lib-flexible的使用)
    19-webpack性能优化集锦
    10-map/WeakMap/WeakSet的使用场景
    03-web worker vue项目实战
    ELFhash
    哈希查找
    Logger之Logger.getLogger(CLass)使用(转载)
    mybatis中多对一查询
    IDEA中无法打开查看log文件解决方案
    Self-Supervised Scene De-occlusion(转载)
  • 原文地址:https://www.cnblogs.com/popodynasty/p/14465900.html
Copyright © 2011-2022 走看看