前缀知识

lowbit=x&(-x)

lowbit表示的是转换成二进制取第一个一和后面的0得到的数,树状数组经常用到lowbit(原理我也不知道)

树状数组只支持单点修改和前缀查询,其他可以通过拓展

时间复杂度分析:

单点修改O(log n)

前缀和查询O(log n)

树状数组操作:

1.lowbit();

2.modify(x,d);//用于将点x加上数d

3.query(int x);//表示算出到x的前缀和

经典例题

1.洛谷P3374

裸的树状数组,直接按照需要的操作进行单点修改和前缀和查询,在利用前缀和的性质,来算出区间答案

cpp代码如下(代码中,a[i]表示一开始第i项的初始值,s[i]表示编号为i的数在维护的树状数组里,每个编号的数

#include       <map>
#include       <set>
#include     <ctime>
#include     <cmath>
#include     <queue>
#include     <stack>
#include    <cstdio>
#include    <vector>
#include   <cstring>
#include   <cstdlib>
#include <iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
int a[500005],n,m,s[500005];
int lowbit(int x)
{
    return x&(-x);
}
void modify(int x,int d)
{
    for(int i=x;i<=n;i+=lowbit(i))
    {
        s[i]+=d;
    }
}
int qusry(int x)//返回前x项的前缀和
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i))
    {
        sum+=s[i];
    }
    return sum;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        modify(i,a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        int xx,x,y,k;
        cin>>xx;
        if(xx==1)
        {
            scanf("%d%d",&x,&k);
            modify(x,k);
        }
        if(xx==2)
        {
            scanf("%d%d",&x,&y);
            cout<<qusry(y)-qusry(x-1)<< endl;
        }
    }
    return 0;
}

 2.洛谷P3368

其实就是由单点修改,变成了区间修改,根据差分数组,只需要由单点修改改成双点修改就可以了

cpp代码如下(带注释)

#include       <map>
#include       <set>
#include     <ctime>
#include     <cmath>
#include     <queue>
#include     <stack>
#include    <cstdio>
#include    <vector>
#include   <cstring>
#include   <cstdlib>
#include <iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
int n,m,a[500001],s[500001],c[500001];//a数组表示第i项的初始值
int lowbit(int x)
{
    return x&(-x);
}
void modify(int x,int d)
{
    for(int i=x;i<=n;i+=lowbit(i))
    {
        c[i]+=d;
    }
}
int qusry(int x)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i))
    {
        sum=sum+c[i];
    }
    return sum;
}
//以上皆为树状数组正常操作
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);//读入初始值
    }
    for(int i=1;i<=n;i++)
    {
        s[i]=a[i]-a[i-1];//维护一个差分数组
        modify(i,s[i]);//将差分数组形成一个新的树状数组
    }
    for(int i=1;i<=m;i++)
    {
        int xx,x,y,k;
        scanf("%d",&xx);
        if(xx==1)
        {
            scanf("%d%d%d",&x,&y,&k);
            modify(x,k);
            modify(y+1,-k);
            //根据差分数组,只需要将他的头指针+k,(尾指针+1)的指针的值-k,就可以将x~y区间的值都加上k
        }
        if(xx==2)
        {
            scanf("%d",&x);
            cout<<qusry(x)<< endl;//查询的这个数就为他的差分数组的前缀和
        }
    }
    return 0;
}