1. 程式人生 > >樹狀數組及二維樹狀數組

樹狀數組及二維樹狀數組

names 方式 結點 分享圖片 cstring cnblogs 區間查詢 ble 常見

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

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

部分內容轉自https://www.cnblogs.com/hsd-/p/6139376.html

一.樹狀數組

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

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

技術分享圖片

然後我們對他進行變形

技術分享圖片

是不是感覺更好理解了呢?

然後我們對其進行標號

技術分享圖片

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

最重要的性質是

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");
    }
}
二維樹狀數組

樹狀數組及二維樹狀數組