zoukankan      html  css  js  c++  java
  • 树状数组进阶-区间修改+区间查询

    树状数组进阶:

    区间修改与区间查询

    今天老糊涂了,树状数组忘记了,基本的只要单点修改+区间查询功能,如果要进行区间加操作,需要把树状数组进行改造。

    我们首先来回顾树状数组的功能:

    lowbit(x&(-x)):返回二进制最低位1的值:比如x=1010那么lowbit值为2

    x+lowbit(x):把最后一位二进制最低位1,往前进一位。

    x-lowbit(x):去掉最后一位二进制最低位1

    我们认为凡是x+lowbit(x)代表父亲节点,x-lowbit(x)代表儿子节点。

    存储过程:

    for (int i=x;i<=n;i+=lowbit(i)){

          sum[i]+=w;

    }

    我们只把值存储加在父亲节点上。

    对于任何一个1-n的值,我们都可以通过这样存储在这样一个树形结构上面。

    C1 = A1

    C2 = A1+A2

    C3 = A3

    C4 = A1+A2+A3+A4

    C5 = A5

    C6 = A5+A6

    C7 = A7

     C8 = A1+A2+A3+A4+A5+A6+A7+A8

    区间求和:

    对于需要求一个区间和[1-n]值,我们以及知道当前节点存储了当前的信息和之前的部分信息,因此我们需要往下不断寻找,而子节点的信息没有父亲节点的信息,从而不断往下查找从而得到答案。

    区间修改:这个以及不能把树状数组再这么更新了,我们知道如果我更新,虽然也会起到区间更新的效果,但本质却并没有对后面产生影响,只不过对区间和产生影响罢了。

    下面来介绍新的树状数组方法:

    引入差分概念:

    c[i]=a[i]-a[i-1]

    那么我对某个区间内部+K实际上等价于区间内部所有c[i]+5

    那么某个元素的值其实等于$a[i]=sum_1^nc[i]$

    那么区间和呢?

    我们可以知道

    $sum_1^na[i]$

    $=a[1]+a[2]+a[3]...a[n]$

    $=a[1]+a[2]+a[3]...a[n]$

    $=c[1]+c[1]+c[2]+c[1]+c[2]+c[3]...c[1]+c[2]...+c[n-1]+c[n]$

    $=c[1]+c[1]+c[2]+c[1]+c[2]+c[3]...c[1]+c[2]...+c[n-1]+c[n]$

    $=n*(c[1]+c[2]+...+c[n])-(c[2]+c[3]+....c[n]+c[3]+...c[n]+...+c[n]+c[n])$

    $=ncdot sum _{1}^nc[i]-sum _1^nc[i]*(i-1)$

    $=sum _1^nc[i]*(n-i+1)$

    因此我们需要维护两个值:差分数组c[i]=a[i]-a[i-1]b[i]=(c[i])*(i-1)

    单点更新:

    C[i]还是与原来的树状数组一样更新,c[i]+x即可

    b[i]=c[i]*(i-1)那么b[i]=(c[i]+x)*(i-1)b[i]=(c[i])*(i-1)+x*(i-1)我们更新x*(i-1)即可

    更新效果:把x位置后面所有的数的值+w

    区间更新:

    [L,R]+w=update(r,w)-update(l-1,w)

    更新效果:把l位置到r位置所有的数的值+w

    区间求和:

    由上面证明可知:

    $sum_1^n a[i] = n*sum_1^n c[i] - sum_1^n b[i] $

    和以前一样求和即可,求和式子换成上面那种即可

    更新效果:$sum(x)=sum_1^x a[i]$

    模板:

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int sum1[1000];
    int sum2[1000];
    int a[1000];
    int n,m;
    int lowbit(int x){
      return x&(-x);
    }
    void update(int x,int w){//更新效果:把x位置后面所有的数的值+w
       for (int i=x;i<=n;i+=lowbit(i)){
          sum1[i]+=w;//维护前缀和c[i]
          sum2[i]+=w*(x-1);//维护前缀和c[i]*(n-1)
       }
    }
    void range_update(int l,int r,int val)//更新效果:把l位置到r位置所有的数的值+w
    {
        update(l,val);
        update(r+1,-val);
    }
    int sum(int x){//求1-x的和
      int ans=0;
      for (int i=x;i>0;i-=lowbit(i)){
        ans+=x*sum1[i]-sum2[i];
      }
      return ans;
    }
    int range_ask(int l,int r){//求l-r的和
       return sum(r)-sum(l-1);
    }
    int main(){
      while(~scanf("%d%d",&n,&m)){
        for (int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            update(i,a[i]-a[i-1]);//维护差分数组
        }
       }
      return 0;
    }

    ---恢复内容结束---

    有不懂欢迎咨询 QQ:1326487164(添加时记得备注)
  • 相关阅读:
    DNS
    NTP服务
    DHCP服务
    NFS服务、SSHD服务
    samba 、 FTP 、 lrzsz工具
    centos7系统忘记root密码
    linux系统rpm和yum软件包管理
    linux系统命令(调试命令)(nmtui,ip a、ss、ps、uptime、top、lsof、grep,iotop、iftop)
    浅聊TCP的三次握手和四次挥手
    C语言学习笔记--动态库和静态库的使用
  • 原文地址:https://www.cnblogs.com/bluefly-hrbust/p/10416808.html
Copyright © 2011-2022 走看看