1. 程式人生 > >樹狀數組區間修改,區間更新:差分數組的運用

樹狀數組區間修改,區間更新:差分數組的運用

容易 div 前綴和 根據 www 數組 一個數 log 一個

樹狀數組最原始的作用就是求前綴和,可以實現單點修改和區間查詢。

但是假設現在有:

1.區間修改,單點查詢

2.區間修改,區間查詢

但是又不想敲線段樹怎麽辦?

就用樹狀數組嘍。

假設現在有一個原數組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,所以就對差分數組的前綴和進行修改

設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] += x,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

樹狀數組區間修改,區間更新:差分數組的運用