樹狀數組的區間修改和區間查詢問題

本文章適合已經會基本的樹狀數組用法看。

樹狀數組最經常的兩個操作:單點更新,區間求和
如果要區間更新怎麼辦?
參考文章:https://www.cnblogs.com/kickit/p/9172189.html

(問題一)給定數組 a,有 2 種操作

  1. 將區間 [l,r] 的 a 加上 v
  2. 求區間 i=lrai\sum_{i=l}^{r}a_{i}

解法上面博客已經說明,就不再加以說明。

(問題二)給定數組a,b;有 2 種操作,

  1. 將區間 [l,r] 的 a 加上 v

  2. i=lraibi\sum_{i=l}^{r}a_{i}*b_{i}

分析:
該問題直接差分是不行的,因爲多了一個權值b,然而因爲要進行區間修改操作,我們依然可以先化成差分的形式。
di=aiai1d_{i}=a_{i}-a_{i-1}

i=1xaibi\sum_{i=1}^{x}a_{i}*b_{i} = i=1xj=1idjbi\sum_{i=1}^{x}\sum_{j=1}^{i}d_{j}*b_{i}

這個式子直觀上不太好看,我考慮將他拆開(數學太差,只能靠肉眼觀察QaQ)

= b1(d1)+b2(d1+d2)+..+bx(d1+d2+..+dx)b_{1}(d_{1})+b_{2}(d_{1}+d_{2})+..+b_{x}(d_{1}+d_{2}+..+d_{x})

肉眼發現將 d 提出來會得到一個有意思的東西

= d1(b1+b2+..+bx)+d2(b2+..+bx)+..+dx(bx)d_{1}(b_{1}+b_{2}+..+b_{x})+d_{2}(b_{2}+..+b_{x})+..+d_{x}(b_{x})

si=j=1ibjs_{i} = \sum_{j=1}^{i}b_{j}

上式

= i=1xdi(sxsi1)\sum_{i=1}^{x}d_{i}*(s_{x}-s_{i-1})

= i=1xdisxi=1xdisi1\sum_{i=1}^{x}d_{i}*s_{x} - \sum_{i=1}^{x}d_{i}*s_{i-1}

= sxi=1xdii=1xdisi1s_{x}\sum_{i=1}^{x}d_{i} - \sum_{i=1}^{x}d_{i}*s_{i-1}

則該式子和問題1的式子變得一模一樣,該問題就解決了。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N],s[N],val1[N],val2[N],n;
//樹狀數組部分, val1維護d , val2維護 di * si-1
inline int lowbit(int x){
    return x&(-x);
}
void update(int x,int v,int *val){
    for(int i=x;i<=n;i+=lowbit(i))
        val[i]+=v;
}
int sum(int x,int *val){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=val[i];
    return ans;
}
int main(){

    scanf("%d",&n);
    for(int i=1;i<=n;i++){  //設a的初始值爲0,這裏輸入的是b
        int x;
        scanf("%d",&s[i]);
        s[i]+=s[i-1];
    }
    int m;
    scanf("%d",&m);
    while(m--){
        int op,x,y,v;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1) {
            scanf("%d",&v);
            update(x,v,val1);
            update(y+1,-v,val1);
            update(x,v*s[x-1],val2);
            update(y+1,-v*s[y],val2);
        } else {
            int ans;
            ans = s[y]  *sum(y,val1)   - sum(y,val2);
            ans-= s[x-1]*sum(x-1,val1) - sum(x-1,val2);
            printf("%d\n",ans);
        }

    }
    return 0;
}
/**給出一組測試數據
6
1 2 3 4 5 6
4
1 2 3 3
2 3 4
1 1 5 3
2 2 6
*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章