樹狀數組及二維樹狀數組

一直覺得樹狀數組能用線段樹水過去,直到我今天碰上了樹狀數組模板題。html

而後就是開始認真的學習樹狀數組,忽然發現怎麼這麼好寫qwqqqq。ios

部分圖片轉自http://www.javashuo.com/article/p-aiipxaiq-kd.html數組

一.樹狀數組數據結構

樹狀數組是一種數據結構,核心思想是利用二進制的補碼思想。ide

首先就是樹狀數組的結構圖函數

而後咱們對他進行變形學習

是否是感受更好理解了呢?spa

而後咱們對其進行標號3d

c數組表示的是記錄的值,A數組表示的是原序列。而後就是全部關於樹狀數組的博客都會有的c數組的存值的展現,博主較懶就不寫了,具體看推薦博客。code

最重要的性質是

C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k爲i的二進制中從最低位到高位連續零的長度)

上面說k是二進制中最低位到最高位的連續零的長度也就是6的二進制是110,那麼k就是1,而後咱們帶入:

c[6]=A[6-2+1]+A[6-2+2]=A[5]+A[6]

看,是否是與上方式子相符,這就是lowbit要實現的功能。那麼,lowbit實現的原理是啥呢?

先粘一下lowbit函數的代碼

1 int lowbit(int k)
2 {
3     return k&(-k);
4 }
lowbit

k&(-k)是啥意思呢?

-k是k的補碼,也就是反碼+1,反碼是啥自行百度,反正也很簡單。

而後你就會發現,你可以取出最小一位的1,而在上面的例子中就是2,你能夠再用幾組數據試一下,發現lowbit(i)=2^i。

4(0100),反碼4(0011),補碼(0100),0100&0100=0100,則lowbit(i)=4,又根據連續0的位數,則k=2,2^2=4,則假設成立。

因此,是否是很是的明白了?

其實還有一種lowbit的寫法,k&(k^(k-1)),能夠本身手推一下。

而後就是單點修改,代碼

1 void add(int x,int val)
2 {
3     while(x<=n)
4     {
5         tree[x]+=val;
6         x+=lowbit(x);
7     }
8 }
單點修改

爲啥是i<=n呢?由於你單點修改是要從葉子結點蹦向父節點的,因此要從小到大。

 1 int sum(int x)
 2 {
 3     int ans=0;
 4     while(x>0)
 5     {
 6         ans+=tree[x];
 7         x-=lowbit(x);
 8     }
 9     return ans;
10 }
區間查詢

那爲啥區間查詢是i!=0呢?由於區間查詢是從父節點蹦向子節點的,因此i要不斷減少,記住區間查詢是查詢的前綴和,因此要查[l,r]的話須要sum(r)-sum(l-1)。

會了以上操做,就先作一下模板題吧 P3374 【模板】樹狀數組 1

若是咱們想區間修改呢?你該不會是枚舉而後add(i)吧,不T纔怪啊!

這時咱們就用到了一種新的思想,差分思想。差分思想是很常見的,下面介紹一下:

數組a[]={1,6,8,5,10},那麼差分數組b[]={1,5,2,-3,5}

也就是說b[i]=a[i]-a[i-1];(a[0]=0;),那麼a[i]=b[1]+....+b[i];(這個很好證的)。

假如區間[2,4]都加上2的話

a數組變爲a[]={1,8,10,7,10},b數組變爲b={1,7,2,-3,3};

發現了沒有,b數組只有b[2]和b[5]變了,由於區間[2,4]是同時加上2的,因此在區間內b[i]-b[i-1]是不變的.

因此對區間[x,y]進行修改,只用修改b[x]與b[y+1]:

b[x]=b[x]+k;b[y+1]=b[y+1]-k;

因此,咱們在存的時候就存差分數組,而後咱們只改變l,與r+1的值,就行了。

那麼單點查詢呢?別忘了樹狀數組存的是前綴和!咱們直接sum(a)。

那差分下的區間查詢呢?直接原數列a[r]-a[l-1]!

二.二維樹狀數組

二維樹狀數組,就是矩陣嘛!你按照矩陣的方式作不就行了!

在add和sum函數裏,兩層循環,一層y一層x,不就行了!

而後就是二位樹狀數組全部的代碼演示

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int a[2000][2000],n;
int lowbit(int k)
{
    return k&(-k);
}
void change(int i,int y)
{
    int j;
    while(i<=n)
    {
        j=y;
        while(j<=n)
        {
            a[i][j]++;
            j+=lowbit(j);
        }
        i+=lowbit(i);
    }
}
int ask(int i,int y)
{
    int ans=0;
    int j;
    while(i!=0)
    {
        j=y;
        while(j!=0)
        {
            ans+=a[i][j];
            j-=lowbit(j);
        }
        i-=lowbit(i);
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(a,0,sizeof(a));
        int m;
        scanf("%d%d",&n,&m);
        while(m--)
        {
            char opt;
            cin >> opt;
            if(opt=='C')
            {
                int x1,x2,y1,y2;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                change(x2+1,y2+1);
                change(x1,y1);
                change(x1,y2+1);
                change(x2+1,y1);
            }
            else
            {
                int x,y;
                scanf("%d%d",&x,&y);
                printf("%d\n",ask(x,y)&1);
            }
        }
        printf("\n");
    }
}
二維樹狀數組
相關文章
相關標籤/搜索