1. 程式人生 > >樹狀陣列【學習B站視訊筆記】

樹狀陣列【學習B站視訊筆記】

樹狀陣列

1、樹狀陣列是什麼

    樹狀陣列是一個查詢和修改複雜度都為log(n)的資料結構。主要用於查詢任意兩位之間的所有元素之和。但是每次只能修改一個元素的值。

2、樹狀陣列幹什麼用的

      最簡單的樹狀陣列就是用來解決動態字首和問題的資料結構。這個陣列是動態的,也就是說這些值在某些時候會發生變化。

3、樹狀陣列具體圖解

     

令這棵樹的結點編號為C1,C2...Cn。令每個結點的值為這棵樹的值的總和,那麼容易發現:

C1 = A1

C2 = A1 + A2

C3 = A3

C4 = A1 + A2 + A3 + A4

C5 = A5

C6 = A5 + A6

C7 = A7

C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

...

C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

這裡有一個有趣的性質:

設節點編號為x,那麼這個節點管轄的區間為2^k(其中k為x二進位制末尾0的個數)個元素。因為這個區間最後一個元素必然為Ax,

所以很明顯:Cn = A(n – 2^k + 1) + ... + An

舉例:

13=1101=A[13];

往前找:12=1100=A[9]+A[10]+A[11]+A[12];

繼續往前找 8=1000=A[1]+..A[8];

可以看出是逐漸消1致0得出的結果。

可以用Lowbit:x&(-x)實現。

同時從這裡的樹也可以分析出來:對一個值的改變能夠影響的總體是沿著樹往上走的,

每一次都向非0位過後的第一個最小的0變為1上升:10001100=>10011100.

目前不知道具體這個上升這麼操作的原因是什麼。//PS:有大神知道能否告訴我一下 靴靴。

HDU 1166

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set> 
using namespace std; 

int sum[200050];
int n;
int lowbit(int x)
{
	return x&(-x);
} //返回最小的值

void update(int pos,int val)//單點修改 
{
	while(pos<=n)
	{
		sum[pos]+=val;
		pos+=lowbit(pos);
	}
}//維護的是字首和 

int query(int pos)
{
	if(pos==0)return 0;
	int s=0;
	while(pos)
	{
		s+=sum[pos];
		pos-=lowbit(pos);
	}
	return s;	
}//就算是前面的更新,也只是把需要用到的更新了,求區間和的時候需要按1的位置加起來。 

int main()
{
    string s;
    int T;
    cin>>T;
    int i,j;
	int cnt=0,pre;
	while(T--)
    {
    	cin>>n; 
    	printf("Case %d:\n",++cnt);
    	memset(sum,0,sizeof(sum));
		for(int k=1;k<=n;k++)
    	{
	    	scanf("%d",&pre);
	    	update(k,pre);
	    }
	    cin>>s;
	    while(s[0]!='E') 
	    {
	    	if(s[0]=='A')
	    	{
	    		cin>>i>>j;
	    		update(i,j);
	    	}
	    	if(s[0]=='S')
	    	{
	    		cin>>i>>j;
	    		update(i,-j);
	    	}
	    	if(s[0]=='Q')
	    	{
	    		cin>>i>>j;
	    		cout<<query(j)-query(i-1)<<endl;
	    	}
    		cin>>s;
    	}
    	
    }
}