數據結構,是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 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倍);缺點則在於原理複雜(晦澀難懂),且功能較爲侷限.
(可是用來卡常數卻是還不錯)