关于树状数组区间修改和查询详解
2017-06-23 01:07
330 查看
原版介绍
这里我想再加上自己的理解,解释一下。
现在我们需要的是把某个区间的值修改,然后查询
我们设原数组为
a[1] a[2] …. a
我们添加一个c1数组 来记录此项与前一项的差值 即
c1[i] = a[i] - a[i-1]
再添加上c2数组,根据公式c1与c2的关系(下面会说到)
c2[i] = (i-1)*(a[i] - a[i-1])
下面是推导过程:
a[1]+a[2]+…+a
= (c[1]) + (c[1]+c[2]) + … + (c[1]+c[2]+…+c
)
= n * c[1] + (n-1) * c[2] +… +c
= n * (c[1]+c[2]+…+c
) - (0 * c[1]+1* c[2]+…+(n-1) * c
) ①
这就是三者之间关系,根据规律,提取公式
0 * c[1] + 1* c[2] + … + (n-1) * c
→用c2记录 → c2[i] = (i-1)*c[i]
根据①式,我们得出
原式 = n*sigma(c1, n) - sigma(c2, n) //sigma(c1, n)代表c1数组的前n项和
这里才能体现出c2的作用 → 帮助更好的计算
这样前提工作做好了
首先第一个要求是把区间 [left,right] 内所有的数加上 x
这里我们写一个和单个修改类似的函数add
通过这个函数,其实是把从 left 开始以后的数全都加上了 x 所以需要减掉 right 以后的数
由于c1数组变化了,所以c2数组也要变化
同样还是需要减去多加的数字
这样的话就已经完成了修改,只需要把sigma函数写出来输出就可以了
这只是其中一种做法
下面是一个模板例题
例题:线段树练习
代码如下:
这里我想再加上自己的理解,解释一下。
现在我们需要的是把某个区间的值修改,然后查询
我们设原数组为
a[1] a[2] …. a
我们添加一个c1数组 来记录此项与前一项的差值 即
c1[i] = a[i] - a[i-1]
再添加上c2数组,根据公式c1与c2的关系(下面会说到)
c2[i] = (i-1)*(a[i] - a[i-1])
下面是推导过程:
a[1]+a[2]+…+a
= (c[1]) + (c[1]+c[2]) + … + (c[1]+c[2]+…+c
)
= n * c[1] + (n-1) * c[2] +… +c
= n * (c[1]+c[2]+…+c
) - (0 * c[1]+1* c[2]+…+(n-1) * c
) ①
这就是三者之间关系,根据规律,提取公式
0 * c[1] + 1* c[2] + … + (n-1) * c
→用c2记录 → c2[i] = (i-1)*c[i]
根据①式,我们得出
原式 = n*sigma(c1, n) - sigma(c2, n) //sigma(c1, n)代表c1数组的前n项和
这里才能体现出c2的作用 → 帮助更好的计算
这样前提工作做好了
首先第一个要求是把区间 [left,right] 内所有的数加上 x
这里我们写一个和单个修改类似的函数add
void add(ll *a, ll pos, ll x) { while( pos <= n ) { a[pos] += x; pos += lowbit(pos); } }
通过这个函数,其实是把从 left 开始以后的数全都加上了 x 所以需要减掉 right 以后的数
add(c1, left, x); add(c1, right+1, -x);
由于c1数组变化了,所以c2数组也要变化
add(c2, left, (left-1)*x); //更新标记数组 add(c2, right+1, -right*x); //把多余的去掉
同样还是需要减去多加的数字
这样的话就已经完成了修改,只需要把sigma函数写出来输出就可以了
这只是其中一种做法
下面是一个模板例题
例题:线段树练习
代码如下:
#include<cstdio>
#include<iostream>
#define lowbit(x) x&(-x)
#define maxn 200010
#define ll long long
using namespace std;
ll n;
ll a[maxn];//原数组
ll c1[maxn];//差值数组
ll c2[maxn];//根据差值数组根据关系推得的一组数据 起名为标记数组
ll sigma(ll *a, ll pos) {
ll sum = 0;
while(pos > 0) {
sum += a[pos];
pos -= lowbit(pos);
}
return sum;
}
void add(ll *a, ll pos, ll x) { while( pos <= n ) { a[pos] += x; pos += lowbit(pos); } }
int main() {
cin >> n;
for(ll i = 1; i <= n; i++) {
cin &
ab34
gt;> a[i];
add(c1, i, a[i] - a[i-1]); //把该项与前一项的差值记录下来
add(c2, i, (i-1)*(a[i] - a[i-1])); //根据公式来更新标记数组
}
ll t;
cin >> t;
while( t-- ) {
ll index, left, right, x;
cin >> index;
if(index == 1) {
cin >> left >> right >> x;
add(c1, left, x); //更新数组
add(c1, right+1, -x); //由于前面更新过,把多余的减去
add(c2, left, (left-1)*x); //更新标记数组
add(c2, right+1, -right*x); //把多余的去掉
} //右边界都要去掉,因为到该项结束,但是不能修改
if(index == 2) {
cin >> left >> right;
ll sum1 = (left-1)*sigma(c1, (left-1)) - sigma(c2, (left-1));//公式
ll sum2 = right*sigma(c1, right) - sigma(c2, right);
cout << sum2 - sum1 << endl;
}
}
return 0;
}
相关文章推荐
- 树状数组总结——详解(单点/区间查询, 单点/区间修改, 逆序对)
- 树状数组总结——详解(单点/区间查询, 单点/区间修改, 逆序对)
- 关于树状数组区间修改区间查询
- 树状数组的区间修改,单点查询
- POJ 1195 Mobile phones(二维树状数组,点修改,区间查询)
- 树状数组区间修改加区间查询
- 关于树状数组修改区间,求点值
- HDU 4031 Attack(树状数组修改区间查询点)
- POJ 1195 Mobile phones(二维树状数组,点修改,区间查询)
- 【codevs1081】【树状数组】区间修改 单点查询
- [TYVJ P1716/BZOJ 3132 上帝造题的七分钟] 二维树状数组区间修改、区间查询
- 【LuoguP3038/[USACO11DEC]牧草种植Grass Planting】树链剖分+树状数组【树状数组的区间修改与区间查询】
- [POJ 2368 A Simple Problem with Integers] 树状数组区间修改、区间查询
- HUST——1106xor的难题之二(异或树状数组单点修改和区间查询)
- POJ2155【二维树状数组,区间修改,点查询?】【又被输入输出坑】
- 【树状数组】点修改&区间查询
- 【codevs1080】【树状数组】 单点修改 区间查询
- 树状数组 区间修改查询
- Lightoj 1112 - Curious Robin Hood 【单点修改 + 单点、 区间查询】【树状数组 水题】
- POJ 3468 A Simple Problem with Integers 树状数组 区间修改 区间查询