1264. 动态求连续区间和
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。
输入格式
第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。
接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。
数列从 1 开始计数。
输出格式
输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。
数据范围
1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n
输入样例:
10 5 1 2 3 4 5 6 7 8 9 10 1 1 5 0 1 3 0 4 8 1 7 5 0 4 8
输出样例:
11 30 35
树状数组
空间复杂度:O(n),和原始的数组大小一样
时间复杂度:在某一点加值logN,查询某个区间logN
1.每个结点tr[x] 保存的是区间 (x-lowbit(x),x] 的和
2.每个结点tr[x]的父结点是tr[x+lowbit(x)],当一个结点所管理的区间发生改变,就会影响到其父结点,复杂度logN
3.修改操作只能是在某个点+一个值,而不能是直接改变,所要改变可以换成加差值,tr[x] += v-c(v要变成的值,c原来的值)
4.树状数组可以解决的问题都可以用线段树解决,这两者的区别在哪里呢?树状数组的系数要少很多,就比如字符串模拟大数可以解决大数问题,也可以解决1+1的问题,但没人会在1+1的问题上用大数模拟
#include <iostream> #include <algorithm> using namespace std; const int maxn = 1e6+10; int N,M; int tr[maxn]; int lowbit(int x){ return x&-x; //返回只保留二进制的最后一个1之后的值 } void add(int idx,int x){ //向下标为idx的元素+x,同时更新其所影响结点的前缀和 for(int i = idx;i<=N;i += lowbit(i)) //这里的N是数组最后一个下标,有时候不一定是N tr[i] += x; } int query(int idx){ //求下标为idx的前缀和 int sum = 0; for(int i = idx;i>=1;i -= lowbit(i)) sum += tr[i]; return sum; } int main(){ cin>>N>>M; int tmp; for(int i = 1;i<=N;i++){ scanf("%d",&tmp); add(i,tmp); } int op,x,y; while(M--){ scanf("%d%d%d",&op,&x,&y); if(op == 1) add(x,y); else printf("%d\n",query(y)-query(x-1));//前缀和的差 = 区间的和 } return 0; }