树状数组
定义
即二叉索引树或Fenwick树,支持动态单节点修改与连续和查询。
操作
- 建树(Ο(NlogN))
- 查询前缀和(Ο(logN))
- 单节点修改(Ο(logN))
C++实现
先定义函数
lowbit(x)=x&-x
它代表x的二进制表达式中从最右边的1开始的数所对应的二进制值,如38288的二进制为10010101100 10000
加粗部分即为所对应的二进制值,故lowbit(38288)=16
函数定义时用位运算与补码简化了计算。
定义这个函数是为了形成以下的树状结构:
将一根根白条(包括灰节点)囊括节点的总和保存在灰色节点,如12对应的灰色节点储存9~12节点的总和。
而i节点的节点条刚好包括了i-lowbit(i)+1~i的数,如lowbit(12)=8,故包括9~12节点。
定义这样的结构给查询前缀和与修改带来无限方便:
设i对应的灰色节点储存总和为C[i]。
查询前缀和:用递归结构查询,由于是顺次相接的结构,查询i节点前缀和时先查询i对应白条,再递归查询剩余区域。
如查询节点7转化为C[7]+C[6]+C[4]
修改节点:此数设计十分巧妙,一个节点的父节点编号即该节点编号+该节点条长度。而修改只需不停更新父节点即可。
以下为代码,注意建树只需执行n次Add即可。
class BIT
{
public:
int A[SizeofArr];
int C[SizeofArr];
int S;//size
BIT(int Q,int *Ar)
{
S = Q;
int i;
for (i=1;i<=S;i++) A[i]=0;
for (i=1;i<=S;i++) C[i]=0;
for (i=1;i<=S;i++) Add(i,Ar[i]);
}
int Sum(int x)
{
int ret=0;
while (x>0)
{
ret += C[x];
x -= lowbit(x);
}
return ret;
}
void Add(int x,int d)
{
while (x<=S)
{
C[x] += d;
x +=lowbit(x);
}
}
private:
int lowbit(int x)
{
return x&-x;
}
};
例题
1.Ping pong(UVALive4329)
http://acm.hust.edu.cn/vjudge/problem/13895