恩,這是ac的第一道樹狀陣列呢。
如今終於找到了折中方案:樹狀陣列!!!!
**量小,還支援修改!
樹狀陣列也就是二叉索引樹,又被稱為fenwick樹,然而我個人認為它不能被嚴謹地成為樹,因為充其量只是借用的樹形結構的思想,於實現上有著較大的區別。
樹狀陣列雖然運用範圍沒有線段樹那麼廣,但是它的效率要高很多,比如線段樹是$nlogn$,但樹狀陣列是$logn$。
還有一點需要注意的是:樹狀陣列可以區間查詢,但不能運用於任意區間查詢。這一點在後面會提到。
那麼這個樹狀陣列的基本思路就是
用節點ci儲存和,比如:
- c1=a1
- c2=a1+a2
- c3=a3
- c4=a1+a2+a3+a4
- c5=a5
- c6=a5+a6
- c7=a7
- c8=a1+...+a8
當然這樣子可能不是很容易看出內在的聯絡,因此不妨將其轉化為二進位制來觀察:
- c0001=a0001
- c0010=a0001+a0010
- c0011=a0011
- c0100=a0001+a0010+a0011+a0100
- c0101=a0101
- c0110=a0101+a0110
- c0111=a0111
- c1000=a0001+...+a1000
是不是發現了什麼?
沒有嗎?好吧。
事實上這裡的規律就是cn=a(n–2^k+1)+...+an,這裡的k指的是n二進位制末尾0的數量。
獲取2^k的操作我們稱之為lowbit,其實現如下:
int lowbit(int有了lowbit操作之後,求和就很好寫了:k)
1要注意一點,這裡求的ans是區間[1,x]的和,想要[y,x]的和只能$query(x)-query(y-1)$。int query(intx)7
return
ans;
8 }
因此我們回到了之前那個問題:樹狀陣列不能解決所有區間查詢,它只能解決如上的有關聯的區間查詢。
emmmm.....還有update操作:
1這個在明白了樹狀陣列的本質之後也很好理解,就不多做敘述了。void update(int x,int
k)6 }
總的來說,樹狀陣列挺好用的,值得一學。但切記,無論如何都必須掌握線段樹,因為能用樹狀陣列解決的都能用線段樹,而反之不一定如此。
另附ac**見下:
1 #include2 #include3 #include4using
namespace
std;56
const
int maxn=500500;7
8int
n,m;
9int tree[maxn<<2
];10
11int lowbit(int
k)14
15void update(int x,int
k)20}21
22int query(int
x)28
return
ans;29}
3031
intmain()
38for(int i=1;i<=m;i++)
44 }
P3374 模板 樹狀陣列 1 題解
同步 原題鏈結 給定乙個長度為 n nn 的陣列,q qq 組操作 顯然,假設你現在什麼也不會。我們只考慮第 2 22 個操作,即先不考慮修改,如何處理區間和的詢問?顯然,對於初始的陣列 a ia i ai 只需要做乙個字首和 s ss 使得 s j i 1 ja is j sum j a i sj...
題解 P3374 模板 樹狀陣列 1
最簡單的zkw線段樹就十分適合這道題,為什麼用zkw線段樹,可以看一下以下精簡 我們只需要用到單點修改,區間查詢就好了。include define go i,j,n,k for int i j i n i k define fo i,j,n,k for int i j i n i k define...
P3374 模板 樹狀陣列 1
如題,已知乙個數列,你需要進行下面兩種操作 1.將某乙個數加上x 2.求出某區間每乙個數的和 輸入格式 第一行包含兩個整數n m,分別表示該數列數字的個數和操作的總個數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含3個整數,表示乙個操作,具體如下 操作1 ...