一. 线段树
一种二叉树
开四倍空间
#define N 1000000
int sum[N<<2];//求和
int a[N];//存原数组
1.建树(存和)
(1).递归实现
void PushUp(int rt)//更新{Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}
void Build(int l,int r,int rt)
{ //[l,r]当前区间,rt表示当前节点位置
if(l==r) {//若到达叶节点
sum[rt]=a[l];//存储A数组的值
return;
}
int m=(l+r)>>1;
Build(l,m,rt<<1); //左边
Build(m+1,r,rt<<1|1); //右边
PushUp(rt);
}
(2).非递归实现
void Build(int n){
N=1;
while(N < n+2) N <<= 1; //原数组下标+N=存储下标
for(int i=1;i<=n;++i) sum[N+i]=a[i];
for(int i=N-1;i>0;--i){
sum[i]=sum[i<<1]+sum[i<<1|1];
Add[i]=0;
}
}
2.点更新
a[L]+=C
(1).
void Update(int L,int C,int l,int r,int rt)
{
if(l==r){//到达叶节点,修改叶节点的值
sum[rt]+=C;
return;
}
int m=(l+r)>>1;
if(L <= m) Update(L,C,l,m,rt<<1); //根据条件判断往左子树调用还是往右
else Update(L,C,m+1,r,rt<<1|1);
PushUp(rt);//子节点的信息更新然后更新本节点
}
(2).
void Update(int L,int C){
for(int s=N+L;s;s>>=1){
sum[s]+=C;
}
}
3.区间更新
a[L,R]+C
void PushDown(int rt,int ln,int rn){
//ln,rn为左子树,右子树的数字数量。
if(Add[rt]){
Add[rt<<1]+=Add[rt];
Add[rt<<1|1]+=Add[rt];
sum[rt<<1]+=Add[rt]*ln;
sum[rt<<1|1]+=Add[rt]*rn;
Add[rt]=0; //清除标记
}
}
void Update(int L,int R,int C,int l,int r,int rt){
if(L <= l && r <= R){
sum[rt]+=C*(r-l+1);//更新数字和
Add[rt]+=C;//增加Add标记,子区间的Sum仍需要根据Add的值来调整
return ;
}
int m=(l+r)>>1;
PushDown(rt,m-l+1,r-m);//下推标记
//左右子树跟[L,R]有交集则递归
if(L <= m) Update(L,R,C,l,m,rt<<1);
if(R > m) Update(L,R,C,m+1,r,rt<<1|1);
PushUp(rt);//更新本节点信息
}
二. 树状数组
sum[ ]存...如图所示这种
然后神奇的x&(-x)
就求出最小1的位
int lowbit(int x)
{
return x&(-x);
}//
更新
void update(int x, int num)
{
while(x<=n)
{
c[x] += num;
x += lowbit(x);
}
}
int getsum(int x)
{
int s = 0;
while(x>0)
{
s += c[x];
x -= lowbit(x);
}
return s;
}