樹狀數組進階-區間修改+區間查詢


 

樹狀數組進階:

區間修改與區間查詢

今天老糊塗了,樹狀數組忘記了,基本的只要單點修改+區間查詢功能,如果要進行區間加操作,需要把樹狀數組進行改造。

我們首先來回顧樹狀數組的功能:

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])$

$=n\cdot \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;
}

 

---恢復內容結束---


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM