• [笔记]: 树状数组 2017-05-23 15:31 29人阅读 评论(0) 收藏


    这里写图片描述
    主要看图
    i往上找就是i+=2^k; 往下就是减 k是i的二进制右边0的个数
    2^k 直接等于i&(-i);
    传送门:写的很好
    总结
    首先,明白树状数组所白了是按照二分对数组进行分组;维护和查询都是O(lgn)的复杂度,复杂度取决于最坏的情况,也是O(lgn);lowbit这里只是一个技巧,关键在于明白c数组的构成规律;分析的过程二进制一定要深入人心,当作心目中的十进制。

    /*
    树状数组
    定义一个a数组一个c数组 记录和
    c[i]=a[i-2^k+1]+...a[i] k位i的二进制右边0的个数
    计算的时候计算a[1]+..a[r]-(a[1]+...a[l])的和 即为从l开始到r的和 
    用处有两个:
    1.区间查询
    2.单点更新
    
    样例输入:
    5
    1 2 3 4 5 (数组元素) 
    2 4 (左区间和右区间) 
    输出:
    9 (左右区间元素和) 
    */
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int a[1000],c[1000];
    int n;
    int lowbit(int i){
        return i&(-i);
    }
    //这样就可以直接算出2^k(k是2进制i中右边0的个数)
    void change(int i,int x){
        while(i<=n)//一直往上走 如 样例1->2->4->8  5->6->8  
        {
            c[i]+=x;
            i+=lowbit(i);
        }
    }
    int getsum(int x){ 
        int sum=0;
        while(x>0){
            sum+=c[x];
            x-=lowbit(x);
        }
        return sum;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            change(i,a[i]);
        }
        int l;int r;
        scanf("%d%d",&l,&r);
        //cout<<getsum(r)<<endl;
        //cout<<getsum(l-1)<<endl;
        cout<<getsum(r)-getsum(l-1)<<endl;
        return 0;
    }
    

    更新元素计算

    /*
    树状数组 更新元素再求和 
    (更新数组中的元素 求左右区间内的元素和) 
    样例输入:
    5
    1 2 3 4 5 (数组元素) 
    2 4 (左区间和右区间) 
    1   (更新一组) 
    2 5(把二换成5) 
    输出:
    12 (左右区间元素和) 
    */
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int a[1000],c[1000];//一个a数组 一个c数组 
    int n;
    int lowbit(int i){
        return i&(-i);
    }
    //这样就可以直接算出2^k(k是2进制i中右边0的个数)
    void change(int i,int x){
        while(i<=n)
        {
            c[i]+=x;
            i+=lowbit(i);
        }
    }
    void deal(int i,int x){
        int k=x-a[i];//差值 
        while(i<=n){
            c[i]+=k;
            i+=lowbit(i);
        }
    }
    int getsum(int x){ 
        int sum=0;
        while(x>0){
            sum+=c[x];
            x-=lowbit(x);
        }
        return sum;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);//一边读入一遍更新c数组 
            change(i,a[i]);
        }
        int l;int r;
        scanf("%d%d",&l,&r);
            int m;
        scanf("%d",&m);//更新元素的组数 
        for(int i=1;i<=m;i++){
            int p,q;//更新元素的位置和更新后的值 
            scanf("%d%d",&p,&q);
            deal(p,q);
        }
        //cout<<getsum(r)<<endl;
        //cout<<getsum(l-1)<<endl;
        cout<<getsum(r)-getsum(l-1)<<endl;
        return 0;
    }
    

    树状数组求逆序对
    可见此网页

    /*
    样例
    5
    1 5 2 4 3 
    输出 4 
    
    */
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int c[100010];
    struct node{
        int v,p;
    }a[100010];
    int n;
    int lowbit(int i){
        return i&(-i);
    }
    void update(int i){
        while(i<=n)//一直往上走 如 样例1->2->4->8  5->6->8  
        {
            c[i]++;
            i+=lowbit(i);
        }
    }
    int getsum(int x){ 
        int sum=0;
        while(x>0){
            sum+=c[x];
            x-=lowbit(x);
        }
        return sum;
    }
    bool cmp(node xx,node yy){
        return xx.v<yy.v;
    }
    int main(){
        scanf("%d",&n);
        int ans=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i].v);
            a[i].p=i;
        }
        sort(a+1,a+1+n,cmp);
    //排序:离散化 如果a[i].v是10000000 那么c的内存就要到1000000 但是排序以后再找逆序对在内存和时间上更优
        for(int i=1;i<=n;i++){
            update(a[i].p);
            ans+=i-getsum(a[i].p);
            //cout<<i<<"-"<<getsum(a[i].p)<<"="<<ans<<endl;
        }
        cout<<ans;
        return 0;
    }
    
  • 相关阅读:
    允许Traceroute探测 (在防火墙中禁用Time Exceeded类型的ICMP包)
    fatal: not a git repository (or any of the parent directories): .git
    CentOS 7 配置白名单
    CentOS 7 找不到 iptables 文件(需要安装 iptables 服务)
    Grafana 匿名访问(免登录)
    How to install GCC/G++ 8 on CentOS
    【C++ Primer | 16】std::move、std::forward(完美转发)
    安利一个dll库缺失下载的网站
    google glog 源码编译踩坑 以及编译全流程
    VS工程不产生db后缀的文件
  • 原文地址:https://www.cnblogs.com/xljxlj/p/7183649.html
Copyright © 2020-2023  润新知