數據結構:樹狀數組-區間修改區間查詢


樹狀數組的本職工作是修改點,查詢區間和

我們可以先回顧一下姊妹篇:(一維)樹狀數組的實現

然后我們再回顧一下差分數組,差分數組可以實現修改區間,查詢點

如果不用樹狀數組進行優化的話,修改是O(1),查詢是O(n)的

我們要做的就是用樹狀數組把查詢操作優化成對數級別的

這里直接給出樹狀數組的代碼以及差分數組的代碼:

先是樹狀數組的:

int a[maxn];
int c[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y)
{
    while(x<=n)
    {
        c[x]+=y;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

然后是差分數組的:

int a[maxn];
int b[maxn];
int n,q;
void update(int x,int y,int z)
{
    b[x]+=z;
    b[y+1]-=z;
}
int sum(int x)
{
    int ans=0;
    for(int i=1;i<=x;i++)
        ans+=b[i];
    return ans;
}

那么我們只要把他們結合在一起,就是傳說中的差分樹狀數組了,其實很簡單,就是把差分數組存到樹狀數組里面來維護,但是這樣就會有一個問題,雖然查詢的效率變好了

但是修改的效率由原來的O(1)變成了O(logn)的,所以有利有弊需要自己衡量

下面直接給出差分樹狀數組的源碼,其實就是把上述的兩份代碼拼接在了一起

 1 //aininot260
 2 //修改區間,查詢點
 3 #include<iostream>
 4 #include<cstring>
 5 using namespace std;
 6 const int maxn=100005;
 7 const int maxq=100005;
 8 int a[maxn];
 9 int b[maxn];
10 int c[maxn];
11 int lowbit(int x)
12 {
13     return x&(-x);
14 }
15 int n,q;
16 void c_update(int x,int y)
17 {
18     while(x<=n)
19     {
20         c[x]+=y;
21         x+=lowbit(x);
22     }
23 }
24 void update(int x,int y,int z)
25 {
26     b[x]+=z;
27     b[y+1]-=z;
28     c_update(x,z);
29     c_update(y+1,-z);
30 }
31 int sum(int x)
32 {
33     int ans=0;
34     while(x>0)
35     {
36         ans+=c[x];
37         x-=lowbit(x);
38     }
39     return ans;
40 }
41 int main()
42 {
43     cin>>n;
44     for(int i=1;i<=n;i++)
45         cin>>a[i];
46     b[1]=a[1];
47     c_update(1,b[1]);
48     for(int i=2;i<=n;i++)
49     {
50         b[i]=a[i]-a[i-1];
51         c_update(i,b[i]);
52     }
53     cin>>q;
54     while(q--)
55     {
56         int x;
57         cin>>x;
58         if(x==1)
59         {
60             int y,z,w;
61             cin>>y>>z>>w;
62             update(y,z,w);
63         }
64         if(x==2)
65         {
66             int y;
67             cin>>y;
68             cout<<sum(y)<<endl;
69         }
70     }
71     return 0;
72 }

接着要介紹的就是利用樹狀數組來實現區間修改和區間查詢,這樣就可以規避很多必須要寫線段樹的情況

寫完之后發現,帥呆了。時間空間代碼量全部吊打線段樹

寫這種BIT的時候我們需要維護三個數組

long long delta[maxn]; //差分數組 
long long deltai[maxn]; //delta*i 
long long sum[maxn];//原始前綴和

初始化的時候,直接計算前綴和就可以了,兩個樹狀數組都是用來存delta的

然后是修改區間操作,4次Update即可:

            int y,z,w;
            cin>>y>>z>>w;
            update(delta,y,w);
            update(delta,z+1,-w);
            update(deltai,y,w*y);
            update(deltai,z+1,-w*(z+1));

查詢區間和的操作,直接在前綴和基礎上加減delta就可以了

            long long suml=sum[y-1]+y*query(delta,y-1)-query(deltai,y-1);
            long long sumr=sum[z]+(z+1)*query(delta,z)-query(deltai,z);
            cout<<sumr-suml<<endl;

下面給出完整的代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=200005,maxq=200005;
 6 int n,q;
 7 int lowbit(int x)
 8 {
 9     return x&(-x);
10 }
11 long long delta[maxn]; //差分數組 
12 long long deltai[maxn]; //delta*i 
13 long long sum[maxn];//原始前綴和
14 void update(long long *c,int x,int y)
15 {
16     while(x<=n)
17     {
18         c[x]+=y;
19         x+=lowbit(x);
20     }
21 }
22 long long query(long long *c,int x)
23 {
24     long long ans=0;
25     while(x>0)
26     {
27         ans+=c[x];
28         x-=lowbit(x);
29     }
30     return ans;
31 }
32 int main()
33 {
34     cin>>n;
35     for(int i=1;i<=n;i++)
36     {
37         int x;
38         cin>>x;
39         sum[i]=sum[i-1]+x;
40     }
41     cin>>q;
42     while(q--)
43     {
44         int x;
45         cin>>x;
46         if(x==1)
47         {
48             int y,z,w;
49             cin>>y>>z>>w;
50             update(delta,y,w);
51             update(delta,z+1,-w);
52             update(deltai,y,w*y);
53             update(deltai,z+1,-w*(z+1));
54         }
55         if(x==2)
56         {
57             int y,z;
58             cin>>y>>z;
59             long long suml=sum[y-1]+y*query(delta,y-1)-query(deltai,y-1);
60             long long sumr=sum[z]+(z+1)*query(delta,z)-query(deltai,z);
61             cout<<sumr-suml<<endl;
62         }
63     }
64     return 0;
65 }

其實介紹到這里已經差不多了,但是精彩的還在后面,二維樹狀數組!

 


免責聲明!

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



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