zoukankan      html  css  js  c++  java
  • 树状数组 小白篇(2)暨区间修改

    这篇主要来讲一讲树状数组的区间修改

    因为一个一个点改,毫无疑问耗时太长

    所以,机智的人类YY出了用差分来表示数组

    为了便于理解,简单一点数组{an}:a[1]=0, a[2]=0, a[3]=0, a[4]=0, a[5]=0, a[6]=0 ,a[7]=0, a[8]=0, a[9]=0

    用差分思想,delta[x]表示a[x]-a[x-1]

    显然,一开始delta[]=0

    我们先计算出前n项和{sn}来

    然后别眨眼,下面就是见证奇迹的时刻!

    有个分界线显得正式一点


    我们要把a[3]到a[6]整段提高3个单位长度,就让delta[3]+=3, delta[7]-=3

    想象成阶梯,一端抬起,另一端就压下去。(自行脑补)

    那么现在的a[x]+=delta[1]+delta[2]+……+delta[x-1]+delta[x]

    我现在要得到前7项的和,s[7]+=每一项应该加的差分

              暨s[7]+=(delta[1])+(delta[1]+delta[2])+(delta[1])+delta[2]+delta[3])+……+(delta[1]+delta[2]+delta[3]+delta[4]+delta[5]+delta[6]+delta[7])

    我还是画个图吧(为了方便说明只画差分的)

    显然搬过砖的都知道,这个砖不好搬,因为它不方

    那我们就把它变方来

    (为什么那么丑,因为我不会画画以及懒)

    额妹子!现在就好表示这堆砖了!!!!!!!!!

    (7+1)(∑delta【1~7】)-(delta【1】*1+delta【2】*2+……+delta【7】*7)          (注意是7+1,不是7!!!!!!!!!)

    后面的delta【n】*n很难看是不是?

    那就表示成deltai【】

    定义deltai【i】=delta【i】*i

    显然因为要大量求和,所以我们把deltai和delta维护成树状数组

    那么

    1 // 修改:把[l, r]区间均加上x     
    2 Update(delta, l, x);       
    3 Update(delta, r+1, -x);  
    4 Update(deltai, l, x * l);  
    5 Update(deltai, r+1, -x * (r+1)); 
    6 //updata相当于上一篇的add
    // 查询:[l, r]区间和     
    long long suml = s[l - 1] + l * Query(delta, l - 1) - Query(deltai, l - 1);  
    long long sumr = s[r] + (r + 1) * Query(delta, r) - Query(deltai, r);  
    ans=sumr - suml;  
    //query相当于上一篇的sum

    是不是很神奇?

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define lowbit(i) (i&-i)
    typedef long long ll;
    using namespace std;
    const int N = 500005;
    int n,m;
    ll sum[N];
    ll delta[N],deltai[N];
    ll query(ll *p,int pos)
    {
        ll ans=0;
        while(pos>0)
        {
            ans+=p[pos];
            pos-=lowbit(pos);
        }
        return ans;
    }
    void updata(ll *p,int pos,int x)
    {
        while(pos<=n)
        {
            p[pos]+=x;
            pos+=lowbit(pos);
        }
    }
    int main()
    {
        int i,l,r,v;
        ll hhh;
        int s1,s2;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%lld",&hhh),sum[i]=sum[i-1]+hhh;
        while(m--)
        {
            scanf("%lld",&hhh);
            if(hhh==1)                      //1表示修改,l至r闭区间加上v
            {
                scanf("%d%d%d",&l,&r,&v);
                updata(delta,l,v);
                updata(delta,r+1,-v);              //注意边界
                updata(deltai,l,v*l);
                updata(deltai,r+1,-v*(r+1));
            }
            else
            {
                scanf("%d%d",&l,&r);                        //l至r的和
                s1=sum[l-1]+l*query(delta,l-1)-query(deltai,l-1);      //注意边界,不理解就看上面的图
                s2=sum[r]+(r+1)*query(delta,r)-query(deltai,r);
                printf("%lld
    ",s2-s1);
            }
        }
        return 0;
    }

    感谢

    “每天心塞一点点”的博客(我喜欢这个名字)

    参考博文地址

  • 相关阅读:
    Xenu Link Sleuth简单使用
    C#简单游戏外挂制作(以Warcraft Ⅲ为例)
    Silverlight InkPresenter 实现路径回放的探索
    Microsoft Virtual Earth Silverlight Map Control (CTP)快速上手
    【Azure实例】有趣的Silverlight应用:录播简笔画
    Azure Services Platform 入门系列文章索引页
    javascript原型的类继承
    javascript给类添加的方法
    checkbox 全选
    【nodejs】 在 Ubuntu 12.04 安裝 Node.js【转】
  • 原文地址:https://www.cnblogs.com/callmebg/p/7229128.html
Copyright © 2011-2022 走看看