我昨天写树状数组的时候 最初建树的时候是用的原值 没有修改后的值
但是 这样写不能实现区间的加权 必须一个点一个点的更新 很麻烦
然后我百度了一下发现一开始建树存的是数的差值
update(i,a[i] - a[i-1]);
我觉得可能是我的数学理论基础不够扎实 不太能懂 为啥存插值可以很方便的通过update(x,k); update(y+1,-k);的形式实现
新树状数组算法
我在纸上推到了一遍
首先需要弄明白的一个特性,如果按照差分的形式存储,可以通过构建的新数列的∑(1-i)得到a[i]
换言之就是 差分的和(1-i)等同于a[i]这个原来的数
然后 思考 如果要求出原数列的n项和
∑a[i] = ∑ ∑c[j]
1-n j:1-n j:1-i
那么这边就会有很多重复项
根绝这个式子 倒数第三行分解开来
把c[i]和另外一个式子分开来 可以发现第二个式子 c[i]*(i-1)是一个新数 可以专门存它
思路理清楚之后 利用树状数组的特性可以快速求和 与修改
(并不是根据树状数组得到这个特定的表达式 树状数组只是一种用于优化的二进制思想框架
所以 大概可以分为这么几步:
初始化:
update(i,a[i]-a[i-1]); //构造c[i]数列
update(i,(c[i] = a[i]-a[i-1])*[i-1]); //对c[i]这个数列用树状数组构造
区间修改: x->y +k
update(x,k); update(y+1,-k); //对于差分数列的修改依然是老样子
update(x,k*(i-1)); update(y+1,-k*(i-1)); //对于差分数列的修改
原理:∑(c[i]+k)*(i-1) = ∑c[i]*(i-1) + ∑k*(i-1);
查询:就比较方便 利用二进制思想的树状数组 相当于快查 这个不受影响
//
// Created by admin on 2020/3/3.
//
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
vector<int> vec; //原数列
vector<int> c,b;
inline int lowbit(int x){
return x&(-x);
}
inline void init(){
vec.resize(maxn);
c.resize(maxn);
b.resize(maxn);
}
inline void update(int i,int k){
int t_i = i;
while(i < maxn){
c[i] += k;
b[i] += (t_i -1) * k;
i += lowbit(i);
}
}
inline void range_modify(int x,int y,int k){
update(x,k);
update(y+1,-k);
}
inline int query(int x){
int res = 0;
int tx = x;
while(x){
res += tx*c[x] - b[x];
x -= lowbit(x);
}
return res;
}
inline int range_query(int x,int y){
return query(y) - query(x-1);
}
int main(){
init();
int n,m;
cin >> n >> m;
for(int i = 1 ; i <= n ; ++i){
cin >> vec[i];
update(i,vec[i] - vec[i-1]);
}
for(int i = 0 ; i < m ; ++i){
int os;
cin >> os;
if(os == 1){
int x,y,k;
cin >> x >> y >> k;
range_modify(x,y,k);
}else{
int x,y;
cin >> x >> y;
range_query(x,y);
}
}
return 0;
}