树状数组区间修改,区间更新:差分数组的运用

树状数组最原始的做用就是求前缀和,能够实现单点修改和区间查询。html

可是假设如今有:数组

1.区间修改,单点查询spa

2.区间修改,区间查询code

可是又不想敲线段树怎么办?htm

就用树状数组喽。blog

假设如今有一个原数组a(假设a[0] = 0),有一个数组d,d[i] = a[i] - a[i-1],那么博客

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

d数组就是差分数组class

因此求a[i]就能够用树状数组维护d[i]的前缀和查询

区间修改,单点查询:

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

设c是差分数组的前缀和

区间修改:

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; }

比线段树好写多了(蓝儿仍是容易写炸

参考了如下两位前辈的博客,感谢:

https://www.cnblogs.com/lcf-2000/p/5866170.html

https://www.cnblogs.com/RabbitHu/p/BIT.html

相关文章
相关标签/搜索