我昨天寫樹狀數組的時候 最初建樹的時候是用的原值 沒有修改後的值
但是 這樣寫不能實現區間的加權 必須一個點一個點的更新 很麻煩
然後我百度了一下發現一開始建樹存的是數的差值
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;
}