• 刷题笔记-树状数组


    动态求连续区间和 -模板题

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    const int N = 100010;
    int s[N];
    int w[N];
    int n , m;
    int lowbit(int x)
    {
        return x & -x;
    }
    int getSum(int x)
    {
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i))
            res += s[i];
        return res;
    }
    void add(int x,int a)
    {
        for(int i = x; i <= n; i += lowbit(i))
            s[i] += a;
    }
    
    int main(void)
    {
        cin >> n >> m;
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d", &w[i]);
            add(i , w[i]);
        }
        
        int k ,a ,b;
        while(m -- )
        {
            scanf("%d%d%d",&k , &a , &b);
            if(k == 0) printf("%d
    ",getSum(b) - getSum(a - 1));
            else add(a , b);
        }
        
        return 0;
    }
    

    数星星

    思路:

    树状数组用来保存每个x坐标上星星的总数,那么每一颗新星(x,y)的等级为getSum(x)
    然后再将这一颗新星加入树状数组add(x,1);

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    const int N = 32010;
    int s[N];
    int ans[N];
    int lowbit(int x)
    {
        return x & -x;
    }
    int getSum(int x)
    {
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i))
            res += s[i];
        return res;
    }
    void add(int x,int a)
    {
        for(int i = x; i <= 32001; i += lowbit(i))
            s[i] += a;
    }
    
    int main(void)
    {
        int n;
        cin >> n;
        for(int i = 0; i < n; i++)
        {
            int x,y;
            cin >> x >> y;  x++;
            int tmp =  getSum(x);
            ans[tmp]++;
            add(x,1);
        }
        for(int i = 0; i < n; i++)
            cout << ans[i] << endl;
        
        return 0;
    }
    

    小朋友排队

    思路:

    1.最开始想这是一个排序题,和逆序对的数量那一题类似,实际上是一个排序背景下求动态前缀和的问题。
    2.假设序列中逆序对的个数是K,那么交换相邻两个元素最多会使逆序对的数量减少1,因为这种交换只会影响这两个元素,它们相对其他元素的位置相当于没有变化。

    • 说明至少交换K次,才能消除所有的逆序对
      3.冒泡排序是一种交换相邻两个元素的排序,且每一次交换必然使逆序对的个数减一,所以采用冒泡的方式进行排序,可以只交换K次,就能消除所有逆序对
    • 说明最优方案一定是交换了K次,且交换的规则类似于冒泡排序
      4.在冒泡排序中,考虑一个一般元素,发现它的交换次数:在它前面且比它大的元素个数 + 在它后面且比它小的元素个数、
    • 所以本质上,是要我们分别求序列的动态前向&后向前缀和
      5.使用归并排序也可以做,只是难在怎样记录每一个元素的交换次数,因为在排序中一个元素的位置是一直在变化的,所以不能用下标,且元素的值不唯一,也不能使用值作为下标。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1000010;
    
    int a[N],s[N],val[N];
    int n;
    LL ans;
    int lowbit(int x)
    {
        return x & -x;
    }
    void add (int x,int a)
    {
        for(int i = x; i < N; i += lowbit(i))
            s[i] += a;
    }
    int getSum(int x)
    {
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i))
            res += s[i];
        
        return res;
        
    }
    
    int main(void)
    {
        cin >> n;
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&a[i]);
            a[i]++;
            //求在第i个元素之前,且比它大的元素个数
            add(a[i], 1);
            val[i] = getSum(N - 1) - getSum(a[i]);
        }
        memset(s,0,sizeof(s));
        
        for(int i = n-1; i >= 0; i--)
        {
            //求在第i个元素之后,且比它小的元素个数
            add(a[i], 1);
            val[i] += getSum(a[i] - 1);
        }
        
        for(int i = 0; i < n; i++)
            ans += (LL)(1 + val[i])* val[i] / 2;
        
        cout << ans << endl;
        
        return 0;
    }
    
  • 相关阅读:
    MYSQL删除表的记录后如何使ID从1开始
    Python chardet 字符编码判断
    中文搜索引擎技术揭密
    python 处理中文网页时,忽略特殊字符,忽略异常
    cmd 之基础命令
    自己写的删除主键的存储过程
    朝花夕拾delphi的三层结构
    ERWIN中的一对多标识关系和一对多非标识关系
    翻页用的SQL
    关于 Ajax 的一篇通俗易懂的文章
  • 原文地址:https://www.cnblogs.com/zy200128/p/12674413.html
Copyright © 2020-2023  润新知