【模板】树状数组上的差分数组

时间:2022-12-19 16:36:47

数据差分化是一个很神仙也很实用的方法。

具体操作就是将一个数化为多个项的和的形式,这些我们产生的项多为g(x)=f(i)-f(i-1)一类形式,这样可以错位相消去,十分巧妙。

数据差分化有以下神仙之处:

  • 通过差分数据得到原数据g(x):十分显然,g(x)=f(1)+f(2)+f(3)+……+f(x) , ( f(0)=0 )。证明略。

  • 改变区间[ l , r ]的值,只需要进行g(l)=g(l)+k, g(r+1)=g(r+1)-k这样的操作就行了。原因是中间的项全部通过加减抵消了那个加入的k。

因此,维护一个差分化的数据,需要一种能够高效访问区间[ l , r ]的数据结构,那这种数据结构还有什么呢?当然是

线段树&树状数组

对不起我太弱了导致只知道这两个

附上代码(树状数组( 2 ) 的)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
inline ll qr(){
    char c=getchar();
    ll x=0,q=1;
    while(c<48||c>57)
        q=c==45?-1:q,c=getchar();
    while(c>=48&&c<=57)
        x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return q*x;
}
const int maxn=500000+15;
ll data[maxn];
#define lw(x) (x&(-x))
int n,m;
#define RP(t,a,b) for(int t=(a),edd=(b);t<=edd;t++)
inline void add(int pos,int dataq){
    int t=pos;
    while(t<=n){
        data[t]+=dataq;
        t+=lw(t);
    }
    return;
}
inline ll ask(int x){
    ll ret=0;
    int pos=x;
    while(pos){
        ret+=data[pos];
        pos-=lw(pos);
    }
    return ret;
}
int templ,last;
int t1,t2,t3,t4;
int main(){
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
    n=qr();
    m=qr();
    RP(t,1,n){
        templ=qr();
        add(t,templ-last);
        last=templ;
    }
    RP(t,1,m){
        t1=qr();
        if(t1==1){
            t2=qr();
            t3=qr();
            t4=qr();
            add(t2,t4);
            add(t3+1,-t4);
        }
        else{
            t2=qr();
            cout<<ask(t2)<<endl;
        }
    }
    return 0;
}

此类数据结构,应灵活应用,来达到一些神仙的十分之一的水平。

再次感叹我真是太弱了