树状数组的知识总结

单点修改:

  不需要差分

区间修改,单点查询: //参考

  假设现在有一个原数组a(假设a[0] = 0),有一个数组d,d[i] = a[i] - a[i-1],那么

a[i] = d[1] + d[2] + .... + d[i]

d数组就是差分数组

所以求a[i]就可以用树状数组维护d[i]的前缀和

区间修改,单点查询:

根据d的定义,对[l,r]区间加上x,那么a[l]和a[l-1]的差增加了x,a[r+1]与a[r]的差减少了x,所以就对差分数组的前缀和进行修改

扫描二维码关注公众号,回复: 4992092 查看本文章

设c是差分数组的前缀和

for(int i=1;i<=n;i++)
{
    scanf("%d",&y);
    add(i,y-x);
    x=y;
}
void add(int x,int k)
{
    for (int i = 1;i <= n;i += lowbit(i)) c[i] += k;
}
{
    add(l,x);  //差分
    add(r+1,-x);
}
int sum(int x)
{
    int ans = 0;
    for (int i = x;i > 0;i -= lowbit(i)) ans += c[i];
    return ans;
}

区间修改,区间查询:

  根据上面的差分数组的定义可以得到:

a[1] + a[2] + a[3] + ... + a[k] = d[1] + d[1] + d[2] + d[1] + d[2] + d[3] + ... + d[1] + d[2] + d[3] + ... + d[k]

              = Σ(k - i + 1) * d[i]  (i从1到k)

变化一下 Σa[i] (i从1到k) = Σ(k+1) * d[i] - i * d[i] (i从1到k)

d[i]可以用一个前缀和维护,i * d[i]也可以用一个前缀和进行维护,所以区间修改,区间查询就变得很方便了

假设c1维护d[i]的前缀和,c2维护d[i] * i的前缀和

void add(int x,int y)
{
    for (int i = x;i <= n;i += lowbit(i)) c1[i] += y,c2[i] += x * y;
}
{
    add(l,x);
    add(r+1,-x);
}
int sum(int x)
{
    int ans1 = 0;
    int ans2 = 0;
    for (int i = x;i > 0;i -= lowbit(i))
    {
        ans1 += (x + 1) * c1[i];
        ans2 += c2[i];
    }
    return ans1 - ans2;
}

猜你喜欢

转载自www.cnblogs.com/thunder-110/p/10294042.html