zoukankan      html  css  js  c++  java
  • Chapter5树状数组与线段树(补充差分)(待补全两题)

    Chapter 5 树状数组和线段树

    +++

    树状数组

    1.单点修改 2.区间查询

    原数组下标从1开始,假如树状数组是c[],那么c[x], x的二进制表示最后有几个0就是第几层

    假设是第k层。那么c[x] = (x - lowbit(x),x]。lowbit(x) = 2 ^ k

    1.动态求连续区间和 1264
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int a[N], tr[N];
    int n, m;
    
    int lowbit(int x)
    {
        return x & -x;
    }
    
    void add(int x, int y)//加上一个元素
    {
        for(int i = x; i <= n; i += lowbit(i) ) tr[i] += y;
    }
    
    int query(int x)//前缀和
    {
        int res = 0;
        for(int i = x; i; i -= lowbit(i)) res += tr[i];
        return res;
    }
    
    int main()
    {
        cin >> n >> m;
        for(int i = 1; i <= n; i++ ) scanf("%d", a + i);
        //当成将每个元素添加到原本全是0的数组中
        for(int i = 1; i <= n; i++ ) add(i, a[i]);
        
        while(m--)
        {
            int k, x, y;
            cin >> k >> x >> y;
            if(k == 0) cout << query(y) - query(x - 1) << endl;
            else add(x , y);
        }
        return 0;
    }
    
    2.数星星 1265
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 32010;
    int tr[N], level[N];
    
    int lowbit(int x)
    {
        return x & -x;
    }
    
    void add(int x)
    {
        for(int i = x; i <= N; i+= lowbit(i) ) tr[i]++;
    }
    
    int query(int x)
    {
        int res = 0;
        for(int i = x; i ; i -= lowbit(i) ) res += tr[i];
        return res;
    }
    
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0; i < n; i++ )
        {
            int x, y;
            scanf("%d%d", &x, &y);
            x++;//树状数组从下标1开始,整体右移
            level[query(x)]++;
            add(x);
        }
        for(int i = 0; i < n; i++ )
            printf("%d
    ", level[i]);
        
        return 0;
    }
    
    • 线段树

    x-------- 父节点 x / 2 (x >> 1) ---------左儿子2 * x (x << 1) ------ 右儿子2 * x + 1 (x << 1 | 1)

    主要用到的函数:

    1.pushup 用子节点信息更新当前节点信息

    2.build 在一段区间上初始化线段树

    3.modify 修改

    4.query 询问

    1.动态求连续区间和 1264
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int n, m, w[N];
    
    struct Node
    {
        int l, r;
        int sum;
    }tr[N * 4];
    
    
    void pushup(int u)
    {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    
    void build(int u, int l, int r)
    {
        if(l == r)  tr[u] = {l, r, w[r]};//c++ 11
        else
        {
            tr[u] = {l, r};//
            
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
            pushup(u);
        }
    }
    
    int query(int u, int l, int r)
    {
        if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
        
        int mid = tr[u].l + tr[u].r >> 1;
        int sum = 0;
        if(l <= mid) sum += query(u << 1, l, r);
        if(r > mid) sum += query(u << 1 | 1, l, r);
        return sum;
    }
    
    void modify(int u, int x, int v)
    {
        if(tr[u].l == tr[u].r)  tr[u].sum += v;
        else
        {
            int mid = tr[u].l + tr[u].r >> 1;
            if(x <= mid) modify(u << 1, x, v);
            else modify(u << 1 | 1, x, v);
            pushup(u);
        }
    }
    
    int main()
    {
        cin >> n >> m;
        for(int i = 1; i <= n; i++ ) scanf("%d", w + i);
        
        build(1, 1, n);
        
        while(m--)
        {
            int k, a, b;
            scanf("%d%d%d", &k, &a, &b);
            if(k == 0) printf("%d
    ", query(1, a, b));
            else modify(1, a, b);
        }
        
        return 0;
    }
    
    2.数列区间最大值 1270
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <climits>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int n, m, w[N];
    
    struct Node
    {
        int l, r;
        int maxv;
    }tr[N * 4];
    
    void pushup(int u)
    {
        tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
    }
    
    void build(int u, int l, int r)
    {
        if(l == r) tr[u] = {l, r, w[r]};
        else
        {
            tr[u] = {l, r};
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
            pushup(u);
        }
    }
    
    int query(int u, int l, int r)
    {
        if(tr[u].l >= l && tr[u].r <= r) return tr[u].maxv;
        int mid = tr[u].l + tr[u].r >> 1;
        int max1 = INT_MIN;
        if(l <= mid) max1 = max(max1, query(u << 1, l, r));
        if(r > mid) max1 = max(max1, query(u << 1 | 1, l, r));
        
        return max1;
    }
    
    int main()
    {
        cin >> n >> m;
        for(int i = 1; i <= n; i++ ) scanf("%d", w + i);
        
        build(1, 1, n);
        
        int L, R;
        while(m--)
        {
            scanf("%d%d", &L, &R);
            
            printf("%d
    ", query(1, L, R));
        }
        return 0;
    }
    
    
    3.小朋友排队 1215

    因为最终是要从小到大排列,所以核心就是冒泡排序,计算逆序对的数量与每个小朋友需要换位置的次数。

    树状数组tr[]盛放的是每个身高出现的次数

    树状数组tr[]是以身高h[] + 1来当作下标的,因为树状数组下标不可以是0,所以就把身高全部自增1

    树状数组维护的是每个身高出现的次数,刚开始树状数组是0,所以要一次add一个h[],因为之后add进去的元素是不会被之前的元素统计的,所以在讨论h[i]之前比h[i]大的格数是要从前往后遍历,同理计算h[i]之后比h[i]小的数要从后往前遍历,要注意的是统计完一个之后要把树状数组归零。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1e6 + 10;
    int h[N], tr[N];
    int sum[N], n;
    
    int lowbit(int x)
    {
        return x & -x;
    }
    
    void add(int x, int y)
    {
        for(int i = x; i < N; i+= lowbit(i) ) tr[i] += y;
    }
    
    int query(int x)
    {
        int res = 0;
        for(int i = x; i ; i -= lowbit(i)) res += tr[i];
        return res;
    }
    
    int main()
    {
        cin >> n;
        for(int i = 1; i <= n; i++ ) scanf("%d", h + i), h[i]++;
        
        //之前比h[i]大的数的个数
        for(int i = 1; i <= n; i++ )
        {
            sum[i] = query(N) - query(h[i]);
            add(h[i], 1);
        }
        
        memset(tr, 0, sizeof tr);
        
        //之后比h[i]小的数的个数
        for(int i = n; i >= 1; i-- )
        {
            sum[i] += query(h[i] - 1);
            add(h[i], 1);
        }
        
        LL res = 0;
        for(int i = 1; i <= n; i++ ) res += (LL)sum[i] * (1 + sum[i]) / 2;
        
        cout << res << endl;
        return 0;
    }
    
    • 差分

    1.差分 797
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int a[N], b[N];
    int n, m;
    
    void insert(int l, int r, int c)//核心
    {
        b[l] += c;
        b[r + 1] -= c;
    }
    
    int main()
    {
        cin >> n >> m;
        
        for(int i = 1; i <= n; i++ ) scanf("%d", a + i);
        
        for(int i = 1; i <= n; i++ ) insert(i, i, a[i]);
        
        
        int l, r, c;
        while(m--)
        {
            scanf("%d%d%d", &l, &r, &c);
            insert(l, r, c);
        }
        
        for(int i = 1; i <= n; i++ ) a[i] = a[i - 1] + b[i];
        
        for(int i = 1; i <= n; i++ ) printf("%d ", a[i]);
        
        return 0;
    }
    
    2.差分矩阵 798
    //比较简单,了解差分的思想
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1010;
    int a[N][N], b[N][N];
    int n, m, q;
    
    void insert(int x1, int y1, int x2, int y2, int c)
    {
        b[x1][y1] += c;
        b[x2 + 1][y1] -= c;
        b[x1][y2 + 1] -= c;
        b[x2 + 1][y2 + 1] += c;
    }
    
    int main()
    {
        cin >> n >> m >> q;
        for(int i = 1; i <= n; i++ )
            for(int j = 1; j <= m; j++ )
            {
                scanf("%d", &a[i][j]);
                insert(i, j, i, j, a[i][j]);
            }
            
        int x1, y1, x2, y2, c;        
        while(q--)
        {
            scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
            insert(x1, y1, x2, y2, c);
        }
        
        for(int i = 1; i <= n; i++ )
            for(int j = 1; j <= m; j++ )
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];
            
        for(int i = 1; i <= n; i++ )
        {
            for(int j = 1; j <= m; j++ )
            {
             printf("%d ", a[i][j]);   
            }
            cout << endl;
        }    
        return 0;
    }
    
    • else
    螺旋折线 1237
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    int main()
    {
        int x, y;
        cin >> x >> y;
    
        if (abs(x) <= y)  // 在上方
        {
            int n = y;
            cout << (LL)(2 * n - 1) * (2 * n) + x - (-n) << endl;
        }
        else if (abs(y) <= x)  // 在右方
        {
            int n = x;
            cout << (LL)(2 * n) * (2 * n) + n - y << endl;
        }
        else if (abs(x) <= abs(y) + 1 && y < 0)  // 在下方
        {
            int n = abs(y);
            cout << (LL)(2 * n) * (2 * n + 1) + n - x << endl;
        }
        else  // 在左方
        {
            int n = abs(x);
            cout << (LL)(2 * n - 1) * (2 * n - 1) + y - (-n + 1) << endl;
        }
    
        return 0;
    }
    
  • 相关阅读:
    Unity3D-光照贴图技术
    登岳麓山
    第一个OC程序
    Unity3D之碰撞体,刚体
    TypeError: 'stepUp' called on an object that does not implement interface HTMLInputElement
    QQ互联登录回调路径错误redirect uri is illegal(100010)
    Quartz.Net使用
    C# 文件相关操作
    微信扫码支付模式一和模式二的区别
    ankhSVN安装后,VS2010使用
  • 原文地址:https://www.cnblogs.com/scl0725/p/12448275.html
Copyright © 2011-2022 走看看