【入門向】樹狀數組概念理解與例題實現

數據結構,是OIer們永遠沒法避免的一個難關,而簡單數據結構,則是這一切冒險的開始。html

下面咱們就從最簡單的樹狀數組開始學習簡單數據結構吧!ios

概念重要性:較重要數組

概念複雜性:較低數據結構

 

概念:樹狀數組函數

講解連接:樹狀數組學習

理解:lowbit(x)能夠計算出以x爲右端點的由t[x]存儲的數據和所覆蓋的區間寬度(從x-lowbit(x)+1到x)spa

          將數據輸入後構建樹狀數組(因爲t數組默認初始化爲0,因此能夠直接使用修改的Add函數對t數組對應位置進行修改)指針

     每次修改(插入)完一個元素後,都應該對其後求和時包含了它的元素進行修改code

     查詢時則經過跳躍查詢的方式查詢從1到pos的區間和(能夠經過前綴和求區間和的作法來求從s到e的區間和)htm

 

例題:

[Luogu P3374] (模板)數狀數組1

[Luogu P3368] (模板)數狀數組2 //PS:這題須要使用差分思想,對思惟有必定鍛鍊做用

 

具體示例:

 P3374: 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define lowbit(x) x&(-x)//大名鼎鼎的lowbit(函數) 
const int MAXN = 600000; 
using namespace std;
int t[MAXN],size;
void Add(int pos,int n)//給pos位置上的數加上n 
{
    while(pos<=size)//向上循環更新包括pos節點的各節點的值(每次向上跳lowbit(x)) 
    {
        t[pos]+=n;//更新當前指向的節點的值 
        pos+=lowbit(pos);//移動"指針"(然而博主並不會寫指針...) 
    }
}
long long Qry(int pos)//查詢從1到pos的全部數之和 
{
    long long sum=0;
    while(pos)//向下循環求和 
    {
        sum+=t[pos];//將當前節點的值加入總和中 
        pos-=lowbit(pos);//跳過當前pos位置所求出和的部分,對以前還未求和的數字進行處理 
    }
    return sum;
}
int main()
{
    int n;
    ios::sync_with_stdio(false);//關閉流同步... 
    memset(t,0,sizeof(t));//初始化數組 
    cin>>size>>n;//輸入數組大小和操做次數 
    int num;
    for(int i=1;i<=size;i++)
    {    
        cin>>num;//輸入第i位的值 
        Add(i,num);//將num加到第i位上,並向上更新全部包括i的節點 
    }
    for(int i=1;i<=n;i++)//處理n次操做 
    {
        int s,e,ops;
        cin>>ops>>s>>e;//ops=1 ==> 將s處的值加上e ; ops=2 ==> 查詢從s到e的值(閉區間[s,e]) 
        if(ops==1) Add(s,e);//更新(修改) 
        else cout<<Qry(e)-Qry(s-1)<<endl; //查詢([s,e]閉區間能夠表示爲從e到1與從s-1到1的和之差) 
    }
    return 0;
}

 

P3668(無註釋)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define lowbit(x) ((x!=0)?x&(-x):1)
const int MAXN = 600000;
using namespace std;
int d[MAXN]={0},num[MAXN]={0},n;
int Add(int pos,int num)
{
    while(pos<=n)
    {
        d[pos]+=num;
        pos+=lowbit(pos);
    }
}
int query(int pos)
{
    int sum=0;
    while(pos)
    {
        sum+=d[pos];
        pos-=lowbit(pos);
    }
    return sum;
}
int main()
{
    int m,x,y,k,opr;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&opr);
        if(opr==1)
        {
            scanf("%d %d %d",&x,&y,&k);
            Add(x,k); Add(y+1,-k);
        }
        else
        {
            scanf("%d",&x);
            printf("%d\n",num[x]+query(x));
        }
    }
    return 0;
}

還有大量的樹狀數組的經典例題,這裏便不一一列舉了  (話說NOIP2017 [就是那場D1T1是結論題的傻×比賽(差點被逼到退役...)] D2最後一題 正解便是樹狀數組...)

重點:樹狀數組只適用於維護有區間可減性的數據(如求和/逆序對等問題),若是維護的數據不具有這種性質,通常使用這種方法即是錯誤的,這是咱們就須要求助於其它的數據結構啦...

   樹狀數組對於線段樹的優勢在於代碼量少,不易犯錯(對新手較爲友善),同時速度比線段樹快,空間佔用量比線段樹少(1倍VS4倍);缺點則在於原理複雜(晦澀難懂),且功能較爲侷限.

(可是用來卡常數卻是還不錯)

相關文章
相關標籤/搜索