• 树状数组略解


    树状数组

    树状数组是一个很奇特的树,它的节点会比线段树少一些,也能表示一个数组。

    比如一个数组叫做a有4个数,那么它的树状数组样子就长这样

    c数组就是树状数组,能看出来

    c1=a1;
    c2=a1+a2;
    c3=a3;
    c4=a1+a2+a3+a4;
    

    以此类推。。。。。。 很难说出他们的关系,但是如果把它们变为二进制

    c0001=a0001
    c0010=a0001+a0010
    c0011=a0011
    c0100=a0001+a0010+a0011+a0100
    

    你会发现,将每一个二进制,去掉所有高位1,只留下最低位的1,然后从那个数一直加到1,看一看是不是这样。

    树状数组单点修改

    这里有一个很关键的东西,叫做lowbit,lowbit是将一个二进制数的所有高位一都去掉,只留下最低位的1,比如lowbit(5)=lowbit(0101(二进制))=0001(二进制)

    而如果改变x的值,就要加上自己的lowbit,一直加到n,这些节点都要加,比如一共有8个数第3个数要加上k,那么c[0011]+=k;

    c[0011+0001] (c[0100])+=k;

    c[0100+0100] (c[1000])+=k;

    这样就能维护树状数组

    inline int lowbit(register int x)
    {
    	return x&(-x);
     } 
    inline void add(register int x,register int k)
    {
        while(x<=n)
    	{
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
    

    树状数组区间查询

    就是前缀和,比如查询x到y区间的和,那么就将从1到y的和-从1到x的和。

    从1到y的和求法是,将y转为2进制,然后一直减去lowbit(y),一直到0

    比如求1到7的和

    ans+=c[0111];
    ans+=c[0111-0001(0110)];
    ans+=c[0110-0010(0100)];
    ans+=c[0100-0100(c[0]无意义,结束)]
    
    inline int sum(register int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=tree[x];
            x-=lowbit(x);
        }
        return ans;
    }
    

    树状数组区间修改

    简单差分。如果将x到y区间加上一个k,那就是从x到n都加上一个k,再从y+1到n加上一个-k

    加的移动还是i+=lowbit(i);

    代码与单点修改相同(就是两次单点修改)

    树状数组单点查询

    从x点,一直x-=lowbit(x),沿途都加上就好啦

    实际和区间查询相同

    完整代码(Luogu P3374 【模板】树状数组 1

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,tree[2000010];
    inline int lowbit(register int x)
    {
        return x&(-x);
    } 
    inline void add(register int x,register int k)
    {
        while(x<=n)
        {
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
    inline int sum(register int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=tree[x];
            x-=lowbit(x);
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(register int i=1;i<=n;++i)
        {
            int a;
            scanf("%d",&a);
            add(i,a);
        }
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(a==1)
                add(b,c);
            else
            	printf("%d
    ",sum(c)-sum(b-1));
        }
        return 0;
    }
    

    完整代码(Luogu P3368 【模板】树状数组 2

    #include <bits/stdc++.h>
    using namespace std;
    int n,m;
    int input[500010];
    int tree[500100];
    inline int lowbit(register int x)
    {
        return x&(-x);
    } 
    inline void add(register int x,register int k)
    {
        while(x<=n)
        {
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
    inline int search(register int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=tree[x];
            x-=lowbit(x);
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(register int i=1;i<=n;++i)
            scanf("%d",&input[i]);
        while(m--)
        {
            int a;
            scanf("%d",&a);
            if(a==1)
            {
                int x,y,z;
                scanf("%d%d%d",&x,&y,&z);
                add(x,z);
                add(y+1,-z);
            }
            else
            {
                int x;
                scanf("%d",&x);
                printf("%d
    ",input[x]+search(x));
            }
       }
    }
    

    树状数组还珂以求逆序对

    留给读者自行思考(实际我懒着写了,动态开点就行了)

  • 相关阅读:
    多选下拉框带搜索(aps.net)
    asp.net无刷新上传(带预览)
    http免费升级https 攻略(超简单)
    用JS获取地址栏参数的方法
    C#生成二维码
    update更新另一个字段
    自适应瀑布型布局(手机,PC全兼容)
    微信扫码支付.net版本
    常用css3技巧
    Repeater 嵌套,子级Repeater获取 父级Repeater 中的值
  • 原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10073405.html
Copyright © 2020-2023  润新知