树状数组

模板:

const int maxn=10000;

//注意:0 是无效下标
int n;//1-n

//c[i]所掌控的前缀信息的下标范围是[i−lowbit(i)+1,i],树状数组 c 维护 ai 的前缀和
int c[maxn+1];

//lowbit(x):x的二进制表示里最低位的1与更低位的所有0所组成的二进制数的十进制结果
inline int lowbit(int x){ return x & -x; }

//ax+=v
void add(int x,int v) {
    for (int i=x;i<=n;i+=lowbit(i))
        c[i]+=v;
}

//求和a1+a2+...+ax
int sum(int x){
    int s=0;
    for(int i=x;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

//求和al+a(l+1)+...+ar
int sum(int l,int r) { return sum(r)-sum(l-1); }

洛谷3374(模板1) 单点修改+区间查询

#include <cstdio>
using namespace std;
const int maxn=5e5;

int n;
int c[maxn+3];

inline int lowbit(int x){ return x & -x; }


//单点修改
void add(int x,int v) {
    for (int i=x;i<=n;i+=lowbit(i))
        c[i]+=v;
}

//区间查询
int sum(int x){
    int s=0;
    for(int i=x;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

int sum(int l,int r) { return sum(r)-sum(l-1); }

int main(){
    int m,x,a,b;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i){
        scanf("%d",&x);
        add(i+1,x);
    }
    for(int i=0;i<m;++i){
        scanf("%d%d%d",&x,&a,&b);
        if(x==1){
            add(a,b);
        }else {
            printf("%d
",sum(a,b));
        }
    }
    return 0;
}

开始cin cout,TLE,改成scanf后AC。

洛谷3368(模板2)单点查询+区间修改

#include <cstdio>
using namespace std;
const int maxn=5e5;

int n;
int c[maxn+3];

inline int lowbit(int x){ return x & -x; }

void add(int x,int v) {
    for (int i=x;i<=n;i+=lowbit(i))
        c[i]+=v;
}

int sum(int x){
    int s=0;
    for(int i=x;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

void add(int l,int r,int v){ add(l,v); add(r+1,-v); }

int main(){
    int m,x,a,b,k;
    scanf("%d%d",&n,&m);
    int last=0,now;
    for(int i=0;i<n;++i){ //c里不是差分数组,而是差分数组以树状数组的方式存
        scanf("%d",&now);
        add(i+1,now-last);
        last=now;
    }
    for(int i=0;i<m;++i){
        scanf("%d",&x);
        if(x==1){       //[l,r]区间修改变为在差分数组两端打标记
            scanf("%d%d%d",&a,&b,&k);
            add(a,b,k);
        }else {         //单点查询变为对差分数组求前缀和
            scanf("%d",&a);
            printf("%d
",sum(a));
        }
    }
    return 0;
}

洛谷3372(模板3)区间修改+区间查询

 
#include <cstdio>
using namespace std;
const int maxn=1e5;
typedef long long ll;
ll n;
ll c1[maxn+3],c2[maxn+3];//用树状数组c1维护delti前缀和,用树状数组c2维护i⋅delti前缀和

inline ll lowbit(ll x){ return x & -x; }

void add(ll *c,ll x,ll v) {
    for (int i=x;i<=n;i+=lowbit(i))
        c[i]+=v;
}

ll sum(ll *c,ll x){
    ll s=0;
    for(int i=x;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

int main(){
    ll m,x,a,b,k;
    scanf("%lld%lld",&n,&m);
    ll last=0,now;
    for(int i=0;i<n;++i){
        scanf("%lld",&now);
        add(c1,i+1,now-last);
        add(c2,i+1,(i+1)*(now-last));
        last=now;
    }
    for(int i=0;i<m;++i){
        scanf("%lld",&x);
        if(x==1){
            scanf("%lld%lld%lld",&a,&b,&k);
            add(c1,a,k),add(c1,b+1,-k);
            add(c2,a,k*a),add(c2,b+1,-k*(b+1));//delt数组两端加k,i*delt数组两端就要加i*k
        }else {
            scanf("%lld%lld",&a,&b);
            printf("%lld
",((b+1)*sum(c1,b)-sum(c2,b))-(a*sum(c1,a-1)-sum(c2,a-1)));
        }
    }
    return 0;
}

洛谷1908 逆序对(归并排序or树状数组)下面是树状数组code

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=5e5;
typedef long long ll;
int n;
int a[maxn+3],b[maxn+3];
ll c1[maxn+3];

inline ll lowbit(ll x){ return x & -x; }

void add(ll *c,ll x,ll v) {
    for (int i=x;i<=n;i+=lowbit(i))
        c[i]+=v;
}

ll sum(ll *c,ll x){
    ll s=0;
    for(int i=x;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    for(int i=1;i<=n;++i){//离散化
        a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    }
    ll ans=0;
    for(int i=1;i<=n;++i){//边加入边计算,每次加入a[i]对总逆序对的贡献(只向前看)
        add(c1,a[i],1);
        ans+=i-sum(c1,a[i]);//排在a[i]前面,并且大于a[i]的个数
    }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Maxx-el/p/13402259.html