树状数组

树状数组需要一个一维数组,t[pos]表示以pos为最右端点长度为lowbit(pos)的区间的和


如果要改变pos位置的值,与pos位置的值有关的第一个就是t[pos], 第二个是t[pos+lowbit(pos)]:

任意pos1(pos<pos1<pos+lowbit(pos))满足pos1-lowbit(pos1)+1>pos,因为lowbit(pos1)<lowbit(pos),lowbit(pos1)=lowbit(pos1-pos)<=pos1-pos。

所以t[pos1]所对应区间的左端点一定大于pos。

而lowbit(pos+lowbit(pos))>lowbit(pos),即(pos+lowbit(pos))-lowbit(pos+lowbit(pos))+1<=(pos+lowbit(pos))-lowbit(pos),即t[pos+lowbit(pos)]的左端点一定小于等于pos。

于是证明到了与pos的值有关的第二个是t[pos+lowbit(pos)]。

同理,既然t[pos+lowbit(pos)]与pos位置的值有关,就需要更新t[pos+lowbit(pos)],并查找下一个与pos+lowbit(pos)位置的值有关的,那就是

t[(pos+lowbit(pos))+lowbit(pos+lowbit(pos))]........

void chge(int pos,int x){
    while(pos<=N) {
        t[pos]+=x;
        pos+=lb(pos);
    }
}
如果要查询从1到pos位置的值的总和

首先加上t[pos],然后剩下的区间变成1到pos-lowbit(pos)。以此类推。

int q(int pos) {
    int ans=0;
    while(pos>=1) {
        ans+=t[pos];
        pos-=lb(pos);
    }
    return ans;
}

所以这样求x到y区间就可以用q(y)-q(x-1)求得

题目链接1

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=500000+10;
int N,M,num[maxn],t[maxn];

int aa,fl;char cc;
int read() {
    aa=0;cc=getchar();fl=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') fl=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    return aa*fl;
}

int lb(int x){
    return x&(-x);
}

void chge(int pos,int x){
    while(pos<=N) {
        t[pos]+=x;
        pos+=lb(pos);
    }
}

int q(int pos) {
    int ans=0;
    while(pos>=1) {
        ans+=t[pos];
        pos-=lb(pos);
    }
    return ans;
}

int main() {
    N=read();M=read();
    int k,x,y;
    for(int i=1;i<=N;++i) num[i]=read(),chge(i,num[i]);
    for(int i=1;i<=M;++i) {
        k=read();x=read();y=read();
        if(k==1) chge(x,y);
        else printf("%d\n",q(y)-q(x-1));
    }
    return 0;
}

题目链接2

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=500000+10;
int N,M,num[maxn],t[maxn];

int aa,fl;char cc;
int read() {
    aa=0;cc=getchar();fl=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') fl=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    return aa*fl;
}

int lb(int x){
    return x&(-x);
}

void chge(int pos,int x){
    while(pos>=1) {
        t[pos]+=x;
        pos-=lb(pos);
    }
}

int q(int pos) {
    int ans=num[pos];
    while(pos<=N) {
        ans+=t[pos];
        pos+=lb(pos);
    }
    return ans;
}

int main() {
    N=read();M=read();
    int k,x,y,z;
    for(int i=1;i<=N;++i) num[i]=read();
    for(int i=1;i<=M;++i) {
        k=read();
        if(k==1) {
            x=read();y=read();z=read();
            chge(y,z);chge(x-1,-z);
        }
        else {
            x=read();
            printf("%d\n",q(x));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值