先尝试画一个八个节点(下标0-7)的树状数组,下标用二进制。
首先有8个未被支配的节点,他们的最后一位以010101交替出现,所以最后一位是1的节点支配最后一位是0的
然后还剩下未支配的点001 011 101 111,他们倒数第二位以0101交替出现,所以倒数第二位是1的支配倒数第二位是0的
还剩下未被支配节点011 111,由于倒数第三位以0,1交替出现,所以最后是倒数第三位是1的支配倒数第三位是0的
一个树状数组就画好了,用同样的方法推广到更多节点的树状数组会发现,每一个节点的下标,只要把它从右往左数第一个0改成1,就可以得到它父节点的下标,比如010->011, 011->111,把它末尾连续的1去掉,直至遇到第一个0,就可以得到该节点子节点中下标最小的,比如100->100 011->000.
设一个节点下标index的二进制表示为???0111,其中?表示任意0或者1,那么index+1=???1000,所以index|(index+1)=???1111,把从右往左数第一个0改成了1,得到了父节点下标。
而index&(index+1)=???0000,把末尾连续的1全部改为了0,得到了子节点中下标最小的。
其余操作与普通树状数组一样,附样例
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <time.h>
#include <stack>
#include <queue>
using namespace std;
#define LEN 8
int tree[LEN];
int input[]={1,2,3,4,5,6,7,8};
//input[index]增加x
void add(int index,int x)
{
while(index<LEN)
{
tree[index]+=x;
index=index|(index+1);
}
}
//建树
void build()
{
memset(tree,0,sizeof(tree));
for(int i=0;i<LEN;i++)
add(i,input[i]);
}
//求input[0]+input[1]+...+input[index]
int get(int index)
{
int ret=0;
while(index>=0)
{
ret+=tree[index];//ret加上tree[index]所有支配的input[]节点的值之和
index=(index&(index+1))-1;//index更新为tree[index]最小子节点的下标减一
}
return ret;
}
//求input[L]+input[L+1]...+input[R]
int sum(int L,int R)
{
return get(R)-get(L-1);
}
int main()
{
for(int i=0;i<LEN;i++)
input[i]=i+1;
build();
printf("%d\n",sum(0,4));//sum(0,4)=1+2+3+4+5=15
return 0;
}