參考
c[i]代表 子樹的葉子結點的權值之和// 這裡以求和舉例
如圖可以知道
c[1]=a[1];
c[2]=a[1]+a[2];
c[3]=a[3];
c[4]=a[1]+a[2]+a[3]+a[4];
c[5]=a[5];
c[6]=a[5]+a[6];
c[7]=a[7];
c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
將c陣列的結點序號轉化為二進位制
1=(001) c[1]=a[1];
2=(010) c[2]=a[1]+a[2];
3=(011) c[3]=a[3];
4=(100) c[4]=a[1]+a[2]+a[3]+a[4];
5=(101) c[5]=a[5];
6=(110) c[6]=a[5]+a[6];
7=(111) c[7]=a[7];
8=(1000) c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
c[i]=a[i-2^ k+1]+a[i-2^k+2]+…a[i]; (k為i的二進位制中從最低位到高位連續零的長度)例如i=8時,k=3;
換一種巧妙方式標示2^k(k為i的二進位制中從最低位到高位連續零的長度)
比如說t為110時k應該為1這是怎麼做呢??
用t&-t
int lowbit(int t)
//-t 代表t的負數 計算機中負數使用對應的正數的補碼來表示
//例如 :
// t=6(0110) 此時 k=1
//-t=-6=(1001+1)=(1010)
// t&(-t)=(0010)=2=2^1
你可以用幾個例子試一下發現是不是很神奇?
所以公式可以寫成c[i]=a[i-lowbit(i)+1]+a[i-lowbit(i)+2]+…a[i];(替換沒用的2^k)
利用c[i]中介再好好看看上面的圖求a陣列前i項和,,
舉個例子 i=7;
sum[7]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7] ; 前i項和
c[4]=a[1]+a[2]+a[3]+a[4]; c[6]=a[5]+a[6]; c[7]=a[7];
可以推出: sum[7]=c[4]+c[6]+c[7];
序號寫為二進位制: sum[(111)]=c[(100)]+c[(110)]+c[(111)];
舉個例子 i=7; …………………… 111
sum[7]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7] ; 前i項和
c[4]=a[1]+a[2]+a[3]+a[4]; c[6]=a[5]+a[6]; c[7]=a[7];
可以推出: sum[7]=c[4]+c[6]+c[7];
序號寫為二進位制: sum[(111)]=c[(100)]+c[(110)]+c[(111)];
7(111) sum+=c[7]
lowbit(7)=1 7-lowbit(111)=6(110) sum+=c[6]
lowbit(6)=2 6-lowbit(6)=4(100) sum+=c[4]
lowbit(4)=4 4-lowbit(4)=0(000)
再舉個例子 i=5
sum[5]=a[1]+a[2]+a[3]+a[4]+a[5] ; 前i項和
c[4]=a[1]+a[2]+a[3]+a[4]; c[5]=a[5];
可以推出: sum[5]=c[4]+c[5];
序號寫為二進位制: sum[(101)]=c[(100)]+c[(101)];
int getsum(int x)
單點更新
當我們修改a陣列中的某乙個值時 應當如何更新c陣列呢?
回想一下 區間查詢的過程,再看一下上文中列出的圖
結合**分析
void add(int x,int y)
//可以發現 更新過程是查詢過程的逆過程
//由葉子結點向上更新c陣列
如圖:當更新a[1]時 需要向上更新c[1] ,c[2],c[4],c[8]
c[1], c[2], c[4], c[8]
寫為二進位制 c[(001)],c[(010)],c[(100)],c[(1000)]
1(001) c[1]+=a[1]
lowbit(1)=001 1+lowbit(1)=2(010) c[2]+=a[1]
lowbit(2)=010 2+lowbit(2)=4(100) c[4]+=a[1]
lowbit(4)=100 4+lowbit(4)=8(1000) c[8]+=a[1]
做題:單擊傳送門移步hdu敵兵布陣
#include #include #include #include #include #include #include #include #include #define for(a,b) for(int a=0;a> t ;
for(int j=1;j<=t;j++)
cout < s;
if(s[0] == 'e')
break;
scanf("%d%d",&x,&y);
if(s[0] == 'q')
cout << getsum(y)-getsum(x-1)離散化+樹狀陣列求逆序數
#include #include #include using namespace std;
typedef long long ll;
const int n=5e5+10;
struct nodep[n];
int n,bit[n],a[n];
bool cmp(const node&a, const node& b)
return s;}
void solve()
sort(p+1,p+n+1,cmp);//排序
for(int i=1; i<=n; i++)a[p[i].pos]=i;//離散化,a[i]表示原來排序前第i號位置應該放在a[i]位置上
ll ans=0;
for (int i=1; i<=n; i++) bit[i]=0; //初始化樹狀陣列
for(int i=1; i<=n; i++)
printf("%i64d\n",ans);}
int main()
return 0;
}
對這一題給出的**如有疑問參考解釋 樹狀陣列演算法分析
樹狀陣列演算法分析,最近經常碰到樹狀陣列的問題,就不如寫一寫對這個演算法的理解吧 先偷一張圖 大概理解就是,對於 c陣列 是整個樹狀陣列裡的靈魂,也可是tree 陣列,其實a 陣列 是不存在的 可以這麼理解 借勢,c 8 c 4 c 6 c 7 a 8 而 c 4 c 2 c 3 a 4 c 6 c...
樹狀陣列演算法分析
最近經常碰到樹狀陣列的問題,就不如寫一寫對這個演算法的理解吧 先偷一張圖 大概理解就是,對於 c陣列 是整個樹狀陣列裡的靈魂,也可是tree 陣列,其實a 陣列 是不存在的 可以這麼理解 借勢,c 8 c 4 c 6 c 7 a 8 而 c 4 c 2 c 3 a 4 c 6 c 5 a 6 對於 ...
樹狀陣列2 更高深的樹狀陣列
一 區間修改區間求和 這個東西可就不是把 單點查詢區間修改 和 單點修改區間查詢 合起來那麼簡單了,仔細想想,拿之前的辦法都不咋地好做,那麼怎麼辦呢,我們還是要用到差分陣列,設原陣列為a,差分陣列為b,則b 1 a 1 b i a i a i 1 那麼區間修改我們就解決了,只需要log的時間即可,但...