23th 【树状数组】树状数组1&&2

来源:互联网 发布:进出口企业的数据 编辑:程序博客网 时间:2024/05/19 03:23

                                       数状数组两种基本的用法

树状数组其实就是巧妙的利用了二分,有效记录一些段的值。


这图是网上找的,相当的好。
这些长条就是树桩数组记录的和值。
举个例子,想找数组1-11的和,只需要先加数状数组的11,在加10,最后加8。
可以看出来这就是一个每次分一半的数。
实现起来很简单但是有一些部分很巧妙,比如说lowbit,可以快速找到数的父节点或子节点。
不懂可以上网搜一下树状数组,图看懂了基本上就懂了。

首先是单点修改,区间求和。

【题目描述】:

如题,已知有N个元素组成的数列(下标从1开始计数),你需要进行下面两种操作:

1.将某一个数加上x

2.求出某区间每一个数的和

【输入描述】:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3个整数,表示一个操作,具体如下:

操作1: 格式:1 x k 含义:将第x个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

【输出描述】:

输出包含若干行整数,即为所有操作2的结果。(输出不超过int类型)

【样例输入】:

5 51 5 4 2 31 1 32 2 51 3 -11 4 22 1 4

【样例输出】:

1416

【时间限制、数据范围及描述】:

时间:1s 空间:128M

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=100000,M<=100000


#include<iostream>#include<stdio.h>#include<algorithm>using namespace std;const int Maxn=100010;int a[Maxn],t[Maxn];int n,m,x,y,way,k;inline int getint(){    int x=0;char c=getchar();int f=1;    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();    return x*f;}int lowbit(int x){
return x&(-x);
}void update(int x,int val){for(int i=x;i<=n;i+=lowbit(i))t[i]+=val;}//修改int sum(int x){    int ans=0;    for(int i=x;i>=1;i-=lowbit(i))        ans+=t[i];    return ans;}//求和int main(){//freopen("bintreeone.in","r",stdin);//freopen("bintreeone.out","w",stdout);     n=getint();m=getint();     for(int i=1;i<=n;i++)     {a[i]=getint();  update(i,a[i]);}     for(int i=1;i<=m;i++)      {  way=getint();         if(way==1){            x=getint();            y=getint();            update(x,y);}            if(way==2){            x=getint();            y=getint();            printf("%d\n",sum(y)-sum(x-1));        }    }return 0;}

树状数组没有图讲不清楚,但是写起来相当简单,只要记住两个函数和一个lowbit就行了。

其次就是区间修改,单点查询。
为什么只能反过来呢,其实这里我们用差分数组保存,这样区间修改是两个点的修改,而单点查询即为差分数组复原也就是求和。
这样就能利用树状数组的结构了。

【题目描述】:

如题,已知一个数列(下标从1开始计数),你需要进行下面两种操作:

1.将某区间每一个数,加上x

2.获取某一个数的值

【输入描述】:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,第i个数字表示数列第i项的初始值。

接下来M行每行包含2或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x 含义:输出第x个数的值

【输出描述】:

输出包含若干行整数,即为所有操作2的结果。

【样例输入】:

5 51 5 4 2 31 2 4 22 31 1 5 -11 3 5 72 4

【样例输出】:

610

【时间限制、数据范围及描述】:

时间:1s 空间:128M

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=100000,M<=100000

#include<iostream>#include<stdio.h>#include<algorithm>using namespace std;const int Maxn=100010;int a[Maxn],t[Maxn];int n,m,x,y,way,k;inline int getint(){    int x=0;char c=getchar();int f=1;    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();    return x*f;}int lowbit(int x){return x&(-x);}void update(int x,int val){for(int i=x;i<=n;i+=lowbit(i))t[i]+=val;}int sum(int x){    int ans=0;    for(int i=x;i>=1;i-=lowbit(i))        ans+=t[i];    return ans;}int main(){//freopen("bintreetwo.in","r",stdin);//freopen("bintreetwo.out","w",stdout);     n=getint();m=getint();     for(int i=1;i<=n;i++)     { a[i]=getint();   update(i,a[i]-a[i-1]);}       for(int i=1;i<=m;i++)      {  way=getint();         if(way==1){            x=getint();            y=getint();            k=getint();            update(x,k);update(y+1,-k);}            if(way==2){            x=getint();            printf("%d\n",sum(x));        }    }return 0;}