[笔记]: 树状数组

来源:互联网 发布:tfboys三只的关系知乎 编辑:程序博客网 时间:2024/06/14 12:03

这里写图片描述
主要看图
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.单点更新样例输入:51 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;}

更新元素计算

/*树状数组 更新元素再求和 (更新数组中的元素 求左右区间内的元素和) 样例输入:51 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;}

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

/*样例51 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;}
原创粉丝点击