zoukankan      html  css  js  c++  java
  • P3384 轻重链剖分

    复杂数据结构++

    树链剖分,极大地提高了树上元素的查询修改的效率。

    这里是轻重链剖分,通过将对树的dfs,在求出每个节点的父亲,深度,子树大小,重儿子编号的同时,把树分割成了若干条重链,每条链都有独一无二的top节点

    然后将这些链逐一标号映射到序列中,在轻重链剖分下每一个节点及其子树的下表在序列中连续。

    因此就可以通过线段树之类的数据结构在log级别的时间内修改查询

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    #define ll long long
    #define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    using namespace std;
    double pi = acos(-1);
    const double eps = 1e-12;
    const int maxn = 1e5 + 10;
    const int inf = 1e9;
    int read()
    {
        int x = 0, f = 1;
        char c = getchar();
        while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
        while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
        return f * x;
    }
    ll a[maxn], wt[maxn], mod;
    int n, m, S;
    int head[maxn], deep[maxn], fa[maxn], siz[maxn], son[maxn], id[maxn], top[maxn], edge_cnt = 0, cnt = 0;
    
    struct edge {
        int to, next;
    }e[2 * maxn];
    
    inline void add(int from, int to)
    {
        e[++edge_cnt].to = to;
        e[edge_cnt].next = head[from];
        head[from] = edge_cnt;
    }
    
    void dfs1(int now, int f, int dee)//先记录每个点的深度,子树大小,父节点编号,重儿子编号
    {
        deep[now] = dee;
        fa[now] = f;
        siz[now] = 1;
        int maxson = -1;
        for (int i = head[now]; i; i = e[i].next)
        {
            int to = e[i].to;
            if (to == f)continue;
            dfs1(to, now, dee + 1);
            siz[now] += siz[to];
            if (siz[to] > maxson)son[now] = to, maxson = siz[to];
        }
    }
    
    void dfs2(int now, int topf)
    {
        id[now] = ++cnt;
        wt[cnt] = a[now];//将节点值映射到序列
        top[now] = topf;
        if (!son[now])return;
        dfs2(son[now], topf);//先处理重儿子
        for (int i = head[now]; i; i = e[i].next)
        {
            int to = e[i].to;
            if (to == fa[now] || to == son[now])continue;
            dfs2(to, to);//遍历轻儿子,然而轻儿子还是会先遍历重边,所以每一条重链新编号是连续的
        }
        //最终子树的编号也是连续的,因此可以用线段树查询子树和
    }
    
    struct tree {
        int ls;
        int rs;
        long long vue = 0;
        long long lazy = 0;
    }t[4 * maxn];
    void build(int l, int r, int p)
    {
        t[p].ls = l;
        t[p].rs = r;
        if (l == r) { t[p].vue = wt[l] % mod; return; }
        int mid = l + r >> 1;
        build(l, mid, p << 1);
        build(mid + 1, r, (p << 1) + 1);
        t[p].vue = (t[p << 1].vue + t[(p << 1) + 1].vue) % mod;
    }
    inline void spread(int x)//标记下传
    {
        if (t[x].lazy)//如果该节点标记不为0
        {
            t[x * 2].vue += t[x].lazy * (ll)(t[x * 2].rs - t[x * 2].ls + 1);//更新左子区间和
            t[x * 2 + 1].vue += t[x].lazy * (ll)(t[x * 2 + 1].rs - t[x * 2 + 1].ls + 1);//更新右子区间和
            t[x * 2].lazy += t[x].lazy;
            t[x * 2 + 1].lazy += t[x].lazy;//向子节点传递标记
            t[x].lazy = 0;//初始化标记
        }
    }
    void update_add(int x, int y, ll v, int p)
    {
        ll l = t[p].ls;
        ll r = t[p].rs;
        if (l >= x && r <= y)
        {
            t[p].vue += (long long)v * (r - l + 1);
            t[p].lazy += v;
            return;
        }
        spread(p);
        int mid = l + r >> 1;
        if (x <= mid)update_add(x, y, v, p << 1);
        if (y > mid)update_add(x, y, v, (p << 1) + 1);
        t[p].vue = t[p << 1].vue + t[(p << 1) + 1].vue;
    }
    
    long long query(int x, int y, int p)
    {
        int l = t[p].ls;
        int r = t[p].rs;
        if (l >= x && r <= y)
        {
            return t[p].vue % mod;
        }
        int mid = l + r >> 1;
        long long ans = 0;
        spread(p);
        if (x <= mid)ans = (ans + query(x, y, p << 1)) % mod;
        if (y > mid)ans = (ans + query(x, y, (p << 1) + 1)) % mod;
        return ans;
    }
    
    void update_range(int x, int y,ll v)
    {
        v %= mod;
        while (top[x] != top[y])
        {
            if (deep[top[y]] > deep[top[x]])swap(x, y);
            update_add(id[top[x]], id[x], v, 1);
            x = fa[top[x]];
        }
        if (deep[x] > deep[y])swap(x, y);
        update_add(id[x], id[y], v, 1);
    }
    
    ll query_range(int x,int y)
    {
        ll res = 0;
        while (top[x] != top[y])
        {
            if (deep[top[y]] > deep[top[x]])swap(x, y);
            res = (res + query(id[top[x]], id[x], 1)) % mod;
            x = fa[top[x]];
        }
        if (deep[x] > deep[y])swap(x, y);
        res = (res + query(id[x], id[y], 1)) % mod;
        return res;
    }
    
    int main()
    {
        fastio;
        cin >> n >> m >> S >> mod;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i < n; i++)
        {
            int x, y;
            cin >> x >> y;
            add(x, y);
            add(y, x);
        }
        dfs1(S, 0, 1);
        dfs2(S, S);
        build(1, n, 1);
        ll o, x, y, z;
        while (m--)
        {
            cin >> o;
            if (o == 1)
            {
                cin >> x >> y >> z;
                update_range(x, y, z);
            }
            else if (o == 2)
            {
                cin >> x >> y;
                cout << query_range(x, y) << endl;
            }
            else if (o == 3)
            {   
                cin >> x >> z;
                update_add(id[x], id[x] + siz[x] - 1, z, 1);
            }
            else
            {
                cin >> x;
                cout << query(id[x], id[x] + siz[x] - 1, 1) << endl;
            }
        }
        return 0;
    
    }
    
    
  • 相关阅读:
    ssh登录 The authenticity of host 192.168.0.xxx can't be established. 的问题
    linux系统之间互传文件
    Ubuntu16.04上Docker的安装及基本用法
    Ubuntu git 与 gitlab 关联
    Ubuntu E: Sub-process /usr/bin/dpkg returned an error code (1)
    ubuntu16.04搭建jdk1.8运行环境
    Ubuntu18.04 安装Tomcat 8.5
    VMware Ubuntu安装详细过程
    Ubuntu 14.04远程登录服务器--ssh的安装和配置
    Java中文编程开发,让Java编写更改复杂
  • 原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/13353594.html
Copyright © 2011-2022 走看看