1. 程式人生 > >樹狀陣列專題 (單點更新、區間求和) + (區間更新、單點查詢) + (區間更新、區間求和)(差分思想)

樹狀陣列專題 (單點更新、區間求和) + (區間更新、單點查詢) + (區間更新、區間求和)(差分思想)

                                                                                            樹狀陣列專題

一直感覺樹狀陣列用處比較小而且侷限、因為最基本的用法就是單點更新和區間求和、但是線段樹也能做,只不過程式碼長一點,但是仔細的去了解了一下樹狀陣列以後發現有很多很讚的地方值得學習。

1.單點更新、區間求和

for(int x = i; x <= n; x += x&-x) c[x] ++; //a[i]單點更新加一
for(int x = r; x ; x -= x&-x) res += c[x]; //求字首[0,r]

2.區間更新、單點查詢

對於操作C L R d  :在區間L-R上每個值增加d  ,運用差分思想,建立一個初始全為0的b陣列,b[L] += d; b[R+1] -= d;

for(int x = L; x <= n; x += x&-x) b[x] += d;
for(int x = R+1; x <= n; x += x&-x) b[x] -= d;

對於操作Q i   A[i] = a[i] + \sum_{j = 1}^{i} b[j]

int res = a[i];
for(int x = i; x ; x -= x&-x)  res += b[x];

3.區間更新、區間查詢

設S[x] 為字首和,S[x] = \sum_{i = 1}^{x} \sum_{j = 1}^{i} b[j] = (x+1)設S[x]為字首和 c[x] = x*b[x]; 所以有 s[x] = \sum_{i=1}^{x}\sum_{j=1}^{i}b[j] = (x+1)\sum_{i=1}^{x}b[i] - \sum_{i=1}^{x} i * b[i] = (x+1)\sum_{i=1}^{x}b[i] - \sum_{i=1}^{x} c[i]

對於操作 C L R d   b[L] += d; b[R+1] -= d; c[L] += L*d ; c[R+1] += R*d;

for(int x = L; x <= n; x += x&-x) b[x] += d, c[x] += L*d;
for(int x = R+1; x <= n; x += x&-x) b[x] -= d, c[x] -= (R+1)*d;

對於操作 Q L R

int res = s[r] - s[l-1];
for(int x = R; x ; x -= x&-x) res += (R+1)*b[x]-c[x];
for(int x = L-1; x ; x -= x&-x) res -= L*b[x] - c[x];
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e5+100;
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int,int> P;

#define PI 3.1415926
#define sc(x)  scanf("%d",&x)
#define pf(x)  printf("%d",x)
#define pfn(x) printf("%d\n",x)
#define pfs(x) printf("%d ",x)
#define rep(n) for(int i = 0; i < n; i++)
#define per(n) for(int i = n-1; i >= 0; i--)
#define mem(a,x) memset(a,x,sizeof(a))

int n,q;
LL b[maxn],c[maxn],s[maxn];
int a[maxn];

int main()
{
  sc(n),sc(q);
  mem(s,0LL);
  rep(n) {sc(a[i+1]);s[i+1] = s[i] + a[i+1];}
  mem(b,0LL);
  mem(c,0LL);
  rep(q)
  {
    char key[2];
    int l,r,d;
    scanf("%s%d%d",key,&l,&r);
    if(key[0] == 'C')
    {
      sc(d);
      for(int x = l;x <= n; x += x&-x) {b[x] += d;c[x] += l*d;}
      for(int x = r+1;x <= n; x += x&-x) {b[x] -= d;c[x] -= (r+1)*d;}
    }
    else
    {
      LL res = s[r]-s[l-1];
      //res = sum(r) - sum(l-1)   s[x] = ssb[i] = (x+1)sb[i] - isb[i] = (x+1)sb[i] - sc[i];
      for(int x = r; x ; x -= x&-x)  res += ((r+1)*b[x] - c[x]);
      for(int x = l-1; x ; x -= x&-x) res -= (l*b[x] - c[x]);
      printf("%lld\n",res);
    }
  }
    return 0;
}