樹狀陣列,又稱二進位制索引樹,英文名binary indexed tree。
一、樹狀陣列的用途
主要用來求解數列的字首和,a[0]+a[1]+...+a[n]。
由此引申出三模擬較常見問題:
1、單點更新,區間求值。(hdu1166)
2、區間更新,單點求值。(hdu1556)
3、求逆序對。(hdu2838)
二、樹狀陣列的表示
1、公式表示
設a為乙個已知的數列。c為樹狀陣列。則會有
c[i]=a[j]+...+a[i];j=i&(-i)=i&(i^(i-1))。
2、圖形表示
(注:1、最下面的一行表示陣列a,上面的二進位制表示的部分是c;
2、**於
從以上可以發現:
1、樹狀陣列c是表示普通陣列a的一部分的和。
2、小標為奇數時,c[i]只能管轄乙個a[i]。
3、c[i]的最後乙個數一定是a[i]。
三、樹狀陣列的關鍵**
1、[cpp]view plain
copy
print?
intlowbit(
intx)
這段**可以簡單的理解為是樹狀陣列向前或向後衍生是用的。
向後主要是為了找到目前節點的父節點,比如要將c[4]+1,那麼4+(4&(-4))=8,c[8]+1,8+(8&(-8))=16,
c[16]+1。
向前主要是為了求字首和,比如要求a[1]+...+a[12]。那麼,c[12]=a[9]+...+a[12];然後12-12&(-12)=8,
c[8]=a[1]+...+a[8]。
2、[cpp]view plain
copy
print?
void
modify(
intpos,
intnum)
//pos為陣列下標位置,num為要增加的值
} 這段**是用來更新樹狀陣列的,包括區間更新、單點更新。
就是想剛才所說的,一點更新了,要不斷將父節點也更新。
3、[cpp]view plain
copy
print?
intgetresult(
intpos)
//求a[1]+...+a[pos]
return
sum;
}
這段**用來求解字首和的。
就像剛才說的,求解a[1]+...+a[12],也就是c[12]+c[8]搞定。
四、樹狀陣列的優點
1、原本的長度為n的數列求和時間複雜度為o(n),更改的時間複雜度為o(1)。
樹狀陣列將其優化為o(logn)。在n較大時,效率更高。
2、樹狀陣列編碼簡單。
五、注意
1、樹狀陣列的下標要從1開始。
2、在學習的過程中遇到這麼個問題。不知道為什麼pos+pos&(-pos)就到了pos的父節點,也不知道
為什麼pos-pos&(-pos)就得到了下乙個無聯絡的節點,從而可以得到字首和。
我只能說:我不懂如何證明,這是數學問題了,樹狀陣列的發明者應該就是發現了這點才搞出樹狀
陣列的吧。初學者不妨拋開這點,專注於事實,將上面的圖形自己計算畫一遍,非常有利於理解。
六、符**:
hdu1166
單點更新,區間求值
[cpp]view plain
copy
print?
#include
using
namespace
std;
const
intmaxn=50001;
inta[maxn];
intc[maxn];
intn;
intlowbit(
intt)
void
modify(
intt,
intnum)
} intgetresult(
intt)
return
num;
} void
init()
} intmain()
} } system("pause"
);
return
0;
}
hdu1556
區間更新,單點求值
[cpp]view plain
copy
print?
#include
#include
using
namespace
std;
const
intmaxn=100001;
intc[maxn];
intn;
intlowbit(
intt)
void
insert(
intt,
intd)
} intgetsum(
intt)
return
sum;
} int
main()
for(int
j=1;j
printf("%d\n"
,getsum(n));
} system("pause"
);
return
0;
}
hdu2838
求逆序對
[cpp]view plain
copy
print?
#include
#include
using
namespace
std;
const
intmaxn=100001;
struct
node
tree[maxn];
intn;
intlowbit(
intx)
void
modify(
intx,
inty,
intt)
} __int64
query_cnt(
intx)
//比x小的數的個數
return
sum;
} __int64
query_sum(
intx)
//比x小的所有數之和
return
sum;
} int
main()
} printf("%i64d\n"
,ans);
} system("pause"
);
return
0;
}
七、二維樹狀陣列
c[x][y]=sum(a[i][j])。其中,x-lowbit(x)+1<=i<=x,y-lowbit(y)+1<=j<=y。
例題:hdu1892
二維樹狀陣列一般就是對矩陣的操作,更新、求值。。。
**:[cpp]view plain
copy
print?
#include
#include
using
namespace
std;
const
intmaxn=1005;
intc[maxn][maxn];
intlowbit(
intx)
void
modify(
intx,
inty,
intval)
} } int
getresult(
intx,
inty)
} return
sum;
} int
getval(
intx,
inty)
void
init()
} } int
main()
case
'a':
case
'm':
case
'd':
} } }
system("pause"
);
return
0;
}
八、參考文章
樹狀陣列小結
最普通的樹狀陣列 實現用low bi tlowbit lowbit 的優秀性質 原理很簡單 pragma gcc optimize o3 pragma g optimize o3 include include include define maxn 500005 define ll long lo...
Book 樹狀陣列 小結
差不多花了10天學樹狀陣列,是照著這篇部落格做的題目,還差幾道 1.幾個注意的地方 1 lowbit 0 0 會無限迴圈,會導致tle掉,給輸入進去的x 一下就好 2 當給的x的範圍很大的時候,注意要離散化 2.然後就是兩個基本的操作 1 add 2 sum 3.複雜度是log n 4.現在做到的題...
樹狀陣列學習
之前寫的題也遇到過用樹狀陣列,當時都是現查現學,而且總是搞不懂,今天又遇到了一道求區間和的題,不管最後是不是用樹狀陣列可以a,但是既然已經想到了這,就打算好好學習一下。可惜之前查到的資料都沒有儲存記錄,所以又重新查了些資料,彙總學習如下 文末附上樹狀陣列的詳細 樹狀陣列主要用到的操作 int low...