树状数组区间更新+区间查询+单点查询

来源:互联网 发布:传奇盛世7升8翅膀数据 编辑:程序博客网 时间:2024/04/29 18:10

为了更好地使用复杂度比线段树更加优化的树状数组,所以必须实现树状数组的区间更新,于是小弟在网上找了找神牛的代码,浅浅地分析了分析,但是还遗留了一个问题,所以若有幸有神牛看到此贴,希望解答一下最后的那个问题;


树状数组时间复杂度为O(MlogN), 实际用的时候优于线段树,且写得少。


神牛是引入了差分数组,要维护的差分数组ks[i] = a[i] - a[i-1]; 可以容易得到a[i] = ks[1] + ks[2] + ... + ks[i]; 即前i项和,为方便记为sigma(ks, i),已经可以看到树状数组的影子了,所以求区间和随之得到

a[1] + a[2] + .. + a[n] = sigma(ks, 1) + sigma(ks, 2) + ... + sigma(ks, n);

  = n*ks[1] + (n-1)*ks[2] + ... + 2*ks[n-1] + 1*ks[n];

       =  n*(ks[1] + ks[2] +...+ ks[n]) - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

所以可以得到 sum[n] =n * sigma(ks, n)  - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

令jk[i] = (i-1) * ks[i];

则 sum[n] = n * sigma(ks, n) - sigma(jk, n);

之后便是构造两个树状数组;


int lowbit(int k){return k & -k;}void add(int n, int *c, int k, int va){while(k <= n){c[k] += va;k += lowbit(k);}}//-------------------------------------for(i = 1; i <= n; ++i){add(n, c1, i, jk[i]-jk[i-1]);add(n, c2, i, (i-1)*(jk[i]-jk[i-1]));}

然后进行查询求和


int sigma(int *c, int k){int sum = 0;while(k){sum += c[k];k -= lowbit(k);}return sum;}int getSum(int s, int t){return (t*sigma(c1, t)-sigma(c2, t)) - ((s-1)*sigma(c1, s-1)-sigma(c2, s-1));}

进行单点查询时,只需两个参数均传入该点。


在进行区间更新的时候,神牛市通过两次维护c1,两次c2得到的,但本人推测了几种情况,都不能很好的解释这么做的原因,

void update(int s, int t, int va){add(c1, s, va);add(c1, t+1, -va);add(c2, s, va*(s-1));add(c2, t+1, -va*t);}

虽然很悬玄,但是精巧的代码完美的解决了此类问题,希望有理解为何这么区间更新的大佬可以留下评论解释一波~小弟不胜感激。


2 0