1. 程式人生 > >樹狀數組的基本用法

樹狀數組的基本用法

技術 線段樹 com else sin end 其他 () class

樹狀數組的基本用法和奇技淫巧

樹狀數組是一種修改查找復雜度都是logN的實用的數據結構,大家應該都會,下面放一張熟的不能再熟的圖裝裝樣子

技術分享

樹狀數組最基本的操作:單點修改,前綴查詢原理都懂就不贅述了,貼個代碼。

void add(int pos,int x)
{
    for(;pos<=N;pos+=lowbit(pos))
        c[pos]+=x;
}
int query(int pos)
{
    int sum=0;
    for(;pos;pos-=lowbit(pos))
        sum+=c[pos];
    return
sum; }

有一種進階操作,區間修改,單點查詢

這就要用到差分——讓一個位置的前綴和等於它的值。所以查單點就就是查前綴和。

那如何修改區間呢, 舉個例子 比如我們讓 區間4 5 7加上3

原數組    1 4 5 7 10 -> 1 7 8 10 10

差分數組   1 3 1 2 3 -> 1 6 1 2 0

顯而易見,整個區間加上一個數, 區間內部元素的差是不變的。

在差分數組中,只有這個區間兩頭的數字改變了 (挺好理解的吧)

於是 , 就可以通過單點修改區間的兩端來進行區間修改了。

代碼的話 基本操作和上面是一樣的,只是意義不同了 , 唯一不一樣的是讀入的時候存的是差分數組

for(int i=1;i<=N;i++)
{
    cin>>a[i];
    add(i,a[i]-a[i-1]);
}

現在你們可能有一個大膽的想法 : 怎麽實現區間修改區間查詢

下面就是奇技淫巧時間。 (標題已上線)

還是要用差分數組, 下面我們簡單推個式子。

技術分享

所以我們就可以開兩個數狀數組, 一個裏面放c[i],一個裏面放 i×c[i],就能實現區間查詢了

除了每次修改改的是兩個數組, 其他操作都一樣

下面是代碼

#include<bits/stdc++.h>
#define
ull unsigned long long #define lowbit(a) (a&-a) #define MAXN 200005 using namespace std; int N,M,a[MAXN]; ull c1[MAXN],c2[MAXN]; void add(int pos,ull x) { for(int i=pos;i<=N;i+=lowbit(i)) c1[i]+=x,c2[i]+=x*pos; } ull sum(int pos) //[1,pos]區間和 { ull s=0; for(int i=pos;i;i-=lowbit(i)) s+=c1[i]*(pos+1)-c2[i]; return s; } int main() { ios::sync_with_stdio(false); cin>>N; for(int i=1;i<=N;i++) { cin>>a[i]; add(i,a[i]-a[i-1]); } cin>>M; while(M--) { int P,X,Y; ull Z; cin>>P; if(P==1) { cin>>X>>Y>>Z; add(Y+1,-Z); add(X,Z); } else if(P==2) { cin>>X>>Y; cout<<sum(Y)-sum(X-1)<<endl; } } return 0; }

可以到 http://codevs.cn/problem/1082/ 交一下練練手

好了就說這麽多了。

其實, 我想去學線段樹了。。。

樹狀數組的基本用法